{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 序言\n",
    "<font size=5>如何在用C++部署的时候防止辛苦训练的模型泄露？深度学习模型该如何保密？</font>\n",
    "\n",
    "<font size=5>本文介绍了一种PaddleX提供的简单模型加密方法。虽然距绝对保密还有一定差距，但是毕竟解决了有无问题。</font>\n",
    "\n",
    "<font size=5>本周是国家网络安全宣传周，不妨看看飞桨是如何让深度学习模型变得更安全的～～走起～～</font>\n",
    "\n",
    "\n",
    "## 项目简介\n",
    "\n",
    "前置项目：[安全帽佩戴检测模型训练与一键部署（PaddleX、HubServing）](https://aistudio.baidu.com/aistudio/projectdetail/742090)介绍了基于PaddleX的yolov3_darknet53安全帽检测迁移学习实现。\n",
    "\n",
    "安全帽佩戴检测是计算机视觉在工业安全领域应用的典型场景，本文使用PaddleX进行pp-yolo迁移学习训练，并提供了Python部署、本地C++模型加密部署和PaddleHub-Serving服务化部署三种部署方式。\n",
    "\n",
    "\n",
    "在本文中，也对pp-yolo和yolov3_darknet53在安全帽检测数据集上的迁移学习表现进行了对比。\n",
    "\n",
    "## 关于本项目\n",
    "\n",
    "> 针对项目还存在的改进空间，如其它环境的加密部署方式等，希望大家多交流观点、介绍经验，共同学习进步。[个人主页](https://aistudio.baidu.com/aistudio/personalcenter/thirdview/90149)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 环境准备\n",
    "## 安装工具库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Collecting ipywidgets\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/6b/bb/285066ddd710779cb69f03d42fa72fbfe4352b4895eb6abab551eae1535a/ipywidgets-7.6.5-py2.py3-none-any.whl (121 kB)\n",
      "     |████████████████████████████████| 121 kB 5.4 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: ipython-genutils~=0.2.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipywidgets) (0.2.0)\n",
      "Collecting widgetsnbextension~=3.5.0\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/d7/31/7c1107fa30c621cd1d36410e9bbab86f6a518dc208aaec01f02ac6d5c2d2/widgetsnbextension-3.5.2-py2.py3-none-any.whl (1.6 MB)\n",
      "     |████████████████████████████████| 1.6 MB 3.9 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: ipython>=4.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipywidgets) (7.31.1)\n",
      "Requirement already satisfied: traitlets>=4.3.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipywidgets) (5.1.1)\n",
      "Requirement already satisfied: nbformat>=4.2.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipywidgets) (5.1.3)\n",
      "Collecting jupyterlab-widgets>=1.0.0\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/18/4d/22a93473bca99c80f2d23f867ebbfee2f6c8e186bf678864eec641500910/jupyterlab_widgets-1.0.2-py3-none-any.whl (243 kB)\n",
      "     |████████████████████████████████| 243 kB 2.9 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: ipykernel>=4.5.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipywidgets) (6.5.1)\n",
      "Requirement already satisfied: tornado<7.0,>=4.2 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets) (6.1)\n",
      "Requirement already satisfied: jupyter-client<8.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets) (7.1.2)\n",
      "Requirement already satisfied: argcomplete>=1.12.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets) (2.0.0)\n",
      "Requirement already satisfied: debugpy<2.0,>=1.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets) (1.5.1)\n",
      "Requirement already satisfied: matplotlib-inline<0.2.0,>=0.1.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets) (0.1.3)\n",
      "Requirement already satisfied: importlib-metadata<5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipykernel>=4.5.1->ipywidgets) (4.2.0)\n",
      "Requirement already satisfied: pexpect>4.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (4.7.0)\n",
      "Requirement already satisfied: decorator in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (4.4.2)\n",
      "Requirement already satisfied: setuptools>=18.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (56.2.0)\n",
      "Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (2.0.10)\n",
      "Requirement already satisfied: pickleshare in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (0.7.5)\n",
      "Requirement already satisfied: pygments in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (2.11.2)\n",
      "Requirement already satisfied: backcall in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (0.1.0)\n",
      "Requirement already satisfied: jedi>=0.16 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from ipython>=4.0.0->ipywidgets) (0.17.2)\n",
      "Requirement already satisfied: jsonschema!=2.5.0,>=2.4 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbformat>=4.2.0->ipywidgets) (4.4.0)\n",
      "Requirement already satisfied: jupyter-core in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbformat>=4.2.0->ipywidgets) (4.9.1)\n",
      "Requirement already satisfied: notebook>=4.4.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from widgetsnbextension~=3.5.0->ipywidgets) (5.7.0)\n",
      "Requirement already satisfied: zipp>=0.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from importlib-metadata<5->ipykernel>=4.5.1->ipywidgets) (3.7.0)\n",
      "Requirement already satisfied: typing-extensions>=3.6.4 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from importlib-metadata<5->ipykernel>=4.5.1->ipywidgets) (4.0.1)\n",
      "Requirement already satisfied: parso<0.8.0,>=0.7.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jedi>=0.16->ipython>=4.0.0->ipywidgets) (0.7.0)\n",
      "Requirement already satisfied: pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets) (0.18.1)\n",
      "Requirement already satisfied: attrs>=17.4.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets) (21.4.0)\n",
      "Requirement already satisfied: importlib-resources>=1.4.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets) (5.4.0)\n",
      "Requirement already satisfied: pyzmq>=13 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jupyter-client<8.0->ipykernel>=4.5.1->ipywidgets) (22.3.0)\n",
      "Requirement already satisfied: nest-asyncio>=1.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jupyter-client<8.0->ipykernel>=4.5.1->ipywidgets) (1.5.4)\n",
      "Requirement already satisfied: entrypoints in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jupyter-client<8.0->ipykernel>=4.5.1->ipywidgets) (0.3)\n",
      "Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jupyter-client<8.0->ipykernel>=4.5.1->ipywidgets) (2.8.2)\n",
      "Requirement already satisfied: jinja2 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (2.11.0)\n",
      "Requirement already satisfied: nbconvert in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (6.4.0)\n",
      "Requirement already satisfied: Send2Trash in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (1.8.0)\n",
      "Requirement already satisfied: terminado>=0.8.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.12.1)\n",
      "Requirement already satisfied: prometheus_client in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.12.0)\n",
      "Requirement already satisfied: ptyprocess>=0.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pexpect>4.3->ipython>=4.0.0->ipywidgets) (0.7.0)\n",
      "Requirement already satisfied: six>=1.9.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=4.0.0->ipywidgets) (1.16.0)\n",
      "Requirement already satisfied: wcwidth in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=4.0.0->ipywidgets) (0.1.7)\n",
      "Requirement already satisfied: MarkupSafe>=0.23 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from jinja2->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (2.0.1)\n",
      "Requirement already satisfied: pandocfilters>=1.4.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (1.5.0)\n",
      "Requirement already satisfied: defusedxml in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.7.1)\n",
      "Requirement already satisfied: mistune<2,>=0.8.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.8.4)\n",
      "Requirement already satisfied: nbclient<0.6.0,>=0.5.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.5.10)\n",
      "Requirement already satisfied: bleach in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (4.1.0)\n",
      "Requirement already satisfied: jupyterlab-pygments in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.1.2)\n",
      "Requirement already satisfied: testpath in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.5.0)\n",
      "Requirement already satisfied: webencodings in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (0.5.1)\n",
      "Requirement already satisfied: packaging in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (21.3)\n",
      "Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from packaging->bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets) (3.0.7)\n",
      "Installing collected packages: widgetsnbextension, jupyterlab-widgets, ipywidgets\n",
      "Successfully installed ipywidgets-7.6.5 jupyterlab-widgets-1.0.2 widgetsnbextension-3.5.2\n",
      "\u001b[33mWARNING: You are using pip version 21.3.1; however, version 22.0.3 is available.\n",
      "You should consider upgrading via the '/opt/conda/envs/python35-paddle120-env/bin/python -m pip install --upgrade pip' command.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "!pip install ipywidgets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 解压数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!mkdir MyDataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  inflating: ./MyDataset/images/hard_hat_workers999.png  \r"
     ]
    }
   ],
   "source": [
    "!unzip data/data50329/HelmetDetection.zip -d ./MyDataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 切分数据集\n",
    "### 方案一：使用最新PaddleX的develop分支（不推荐）\n",
    "实现方式已在前置项目[安全帽佩戴检测模型训练与一键部署（PaddleX、HubServing）](https://aistudio.baidu.com/aistudio/projectdetail/742090)中介绍。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# !paddlex --split_dataset --format VOC --dataset_dir MyDataset --val_value 0.2 --test_value 0.1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 方案二：参考develop分支的voc_split.py重写数据集切分代码\n",
    "该做法步骤如下：\n",
    "1. 从pip安装PaddleX\n",
    "2. 将`voc_split.py`import的方法一一找出，在Notebook中运行\n",
    "3. 修改voc_split.py切分数据集时对应的文件目录名"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Collecting paddlex\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/ca/03/b401c6a34685aa698e7c2fbcfad029892cbfa4b562eaaa7722037fef86ed/paddlex-2.1.0-py3-none-any.whl (1.6 MB)\n",
      "     |████████████████████████████████| 1.6 MB 3.5 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: colorama in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (0.4.4)\n",
      "Collecting visualdl>=2.2.2\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/87/c8/10d0d24822637d8e5493a73ad118640530195e45b1c71ae0e60606ff5f0e/visualdl-2.2.3-py3-none-any.whl (2.7 MB)\n",
      "     |████████████████████████████████| 2.7 MB 3.5 MB/s            \n",
      "\u001b[?25hCollecting motmetrics\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/9c/28/9c3bc8e2a87f4c9e7b04ab72856ec7f9895a66681a65973ffaf9562ef879/motmetrics-1.2.0-py3-none-any.whl (151 kB)\n",
      "     |████████████████████████████████| 151 kB 4.7 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: pyyaml in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (5.1.2)\n",
      "Collecting pycocotools\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/75/5c/ac61ea715d7a89ecc31c090753bde28810238225ca8b71778dfe3e6a68bc/pycocotools-2.0.4.tar.gz (106 kB)\n",
      "     |████████████████████████████████| 106 kB 150 kB/s            \n",
      "\u001b[?25h  Installing build dependencies ... \u001b[?25ldone\n",
      "\u001b[?25h  Getting requirements to build wheel ... \u001b[?25ldone\n",
      "\u001b[?25h  Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n",
      "\u001b[?25hRequirement already satisfied: tqdm in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (4.27.0)\n",
      "Requirement already satisfied: scipy in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (1.6.3)\n",
      "Collecting scikit-learn==0.23.2\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/f4/cb/64623369f348e9bfb29ff898a57ac7c91ed4921f228e9726546614d63ccb/scikit_learn-0.23.2-cp37-cp37m-manylinux1_x86_64.whl (6.8 MB)\n",
      "     |████████████████████████████████| 6.8 MB 3.0 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: chardet in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (3.0.4)\n",
      "Requirement already satisfied: flask-cors in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (3.0.8)\n",
      "Collecting lap\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/bf/64/d9fb6a75b15e783952b2fec6970f033462e67db32dc43dfbb404c14e91c2/lap-0.4.0.tar.gz (1.5 MB)\n",
      "     |████████████████████████████████| 1.5 MB 3.6 MB/s            \n",
      "\u001b[?25h  Preparing metadata (setup.py) ... \u001b[?25ldone\n",
      "\u001b[?25hRequirement already satisfied: openpyxl in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (3.0.5)\n",
      "Collecting shapely>=1.7.0\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/9d/4d/4b0d86ed737acb29c5e627a91449470a9fb914f32640db3f1cb7ba5bc19e/Shapely-1.8.1.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (2.0 MB)\n",
      "     |████████████████████████████████| 2.0 MB 2.7 MB/s            \n",
      "\u001b[?25hCollecting paddleslim==2.2.1\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/0b/dc/f46c4669d4cb35de23581a2380d55bf9d38bb6855aab1978fdb956d85da6/paddleslim-2.2.1-py3-none-any.whl (310 kB)\n",
      "     |████████████████████████████████| 310 kB 3.6 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: opencv-python in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlex) (4.1.1.26)\n",
      "Requirement already satisfied: pillow in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddleslim==2.2.1->paddlex) (8.2.0)\n",
      "Requirement already satisfied: matplotlib in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddleslim==2.2.1->paddlex) (2.2.3)\n",
      "Requirement already satisfied: pyzmq in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddleslim==2.2.1->paddlex) (22.3.0)\n",
      "Requirement already satisfied: numpy>=1.13.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from scikit-learn==0.23.2->paddlex) (1.19.5)\n",
      "Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from scikit-learn==0.23.2->paddlex) (2.1.0)\n",
      "Requirement already satisfied: joblib>=0.11 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from scikit-learn==0.23.2->paddlex) (0.14.1)\n",
      "Requirement already satisfied: pre-commit in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (1.21.0)\n",
      "Requirement already satisfied: Flask-Babel>=1.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (1.0.0)\n",
      "Requirement already satisfied: bce-python-sdk in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (0.8.53)\n",
      "Requirement already satisfied: requests in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (2.24.0)\n",
      "Requirement already satisfied: protobuf>=3.11.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (3.14.0)\n",
      "Requirement already satisfied: flake8>=3.7.9 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (4.0.1)\n",
      "Requirement already satisfied: flask>=1.1.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (1.1.1)\n",
      "Requirement already satisfied: shellcheck-py in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (0.7.1.1)\n",
      "Requirement already satisfied: six>=1.14.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (1.16.0)\n",
      "Requirement already satisfied: pandas in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.2.2->paddlex) (1.1.5)\n",
      "Collecting pytest\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/38/93/c7c0bd1e932b287fb948eb9ce5a3d6307c9fc619db1e199f8c8bc5dad95f/pytest-7.0.1-py3-none-any.whl (296 kB)\n",
      "     |████████████████████████████████| 296 kB 2.7 MB/s            \n",
      "\u001b[?25hCollecting pytest-benchmark\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/2c/60/423a63fb190a0483d049786a121bd3dfd7d93bb5ff1bb5b5cd13e5df99a7/pytest_benchmark-3.4.1-py2.py3-none-any.whl (50 kB)\n",
      "     |████████████████████████████████| 50 kB 227 kB/s             \n",
      "\u001b[?25hCollecting flake8-import-order\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/ab/52/cf2d6e2c505644ca06de2f6f3546f1e4f2b7be34246c9e0757c6048868f9/flake8_import_order-0.18.1-py2.py3-none-any.whl (15 kB)\n",
      "Collecting xmltodict>=0.12.0\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/28/fd/30d5c1d3ac29ce229f6bdc40bbc20b28f716e8b363140c26eff19122d8a5/xmltodict-0.12.0-py2.py3-none-any.whl (9.2 kB)\n",
      "Requirement already satisfied: jdcal in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from openpyxl->paddlex) (1.4.1)\n",
      "Requirement already satisfied: et-xmlfile in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from openpyxl->paddlex) (1.0.1)\n",
      "Requirement already satisfied: pycodestyle<2.9.0,>=2.8.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.2.2->paddlex) (2.8.0)\n",
      "Requirement already satisfied: pyflakes<2.5.0,>=2.4.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.2.2->paddlex) (2.4.0)\n",
      "Requirement already satisfied: mccabe<0.7.0,>=0.6.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.2.2->paddlex) (0.6.1)\n",
      "Requirement already satisfied: importlib-metadata<4.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.2.2->paddlex) (4.2.0)\n",
      "Requirement already satisfied: itsdangerous>=0.24 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.1->visualdl>=2.2.2->paddlex) (1.1.0)\n",
      "Requirement already satisfied: click>=5.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.1->visualdl>=2.2.2->paddlex) (7.0)\n",
      "Requirement already satisfied: Jinja2>=2.10.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.1->visualdl>=2.2.2->paddlex) (2.11.0)\n",
      "Requirement already satisfied: Werkzeug>=0.15 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.1->visualdl>=2.2.2->paddlex) (0.16.0)\n",
      "Requirement already satisfied: pytz in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from Flask-Babel>=1.0.0->visualdl>=2.2.2->paddlex) (2019.3)\n",
      "Requirement already satisfied: Babel>=2.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from Flask-Babel>=1.0.0->visualdl>=2.2.2->paddlex) (2.8.0)\n",
      "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddleslim==2.2.1->paddlex) (3.0.7)\n",
      "Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddleslim==2.2.1->paddlex) (1.1.0)\n",
      "Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddleslim==2.2.1->paddlex) (2.8.2)\n",
      "Requirement already satisfied: cycler>=0.10 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddleslim==2.2.1->paddlex) (0.10.0)\n",
      "Requirement already satisfied: pycryptodome>=3.8.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from bce-python-sdk->visualdl>=2.2.2->paddlex) (3.9.9)\n",
      "Requirement already satisfied: future>=0.6.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from bce-python-sdk->visualdl>=2.2.2->paddlex) (0.18.0)\n",
      "Requirement already satisfied: setuptools in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8-import-order->motmetrics->paddlex) (56.2.0)\n",
      "Requirement already satisfied: aspy.yaml in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.2.2->paddlex) (1.3.0)\n",
      "Requirement already satisfied: toml in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.2.2->paddlex) (0.10.0)\n",
      "Requirement already satisfied: cfgv>=2.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.2.2->paddlex) (2.0.1)\n",
      "Requirement already satisfied: identify>=1.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.2.2->paddlex) (1.4.10)\n",
      "Requirement already satisfied: virtualenv>=15.2 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.2.2->paddlex) (16.7.9)\n",
      "Requirement already satisfied: nodeenv>=0.11.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.2.2->paddlex) (1.3.4)\n",
      "Requirement already satisfied: pluggy<2.0,>=0.12 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pytest->motmetrics->paddlex) (0.13.1)\n",
      "Requirement already satisfied: attrs>=19.2.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pytest->motmetrics->paddlex) (21.4.0)\n",
      "Requirement already satisfied: packaging in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pytest->motmetrics->paddlex) (21.3)\n",
      "Collecting iniconfig\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl (5.0 kB)\n",
      "Collecting py>=1.8.2\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl (98 kB)\n",
      "     |████████████████████████████████| 98 kB 2.6 MB/s             \n",
      "\u001b[?25hCollecting tomli>=1.0.0\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl (12 kB)\n",
      "Collecting py-cpuinfo\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/e6/ba/77120e44cbe9719152415b97d5bfb29f4053ee987d6cb63f55ce7d50fadc/py-cpuinfo-8.0.0.tar.gz (99 kB)\n",
      "     |████████████████████████████████| 99 kB 3.5 MB/s             \n",
      "\u001b[?25h  Preparing metadata (setup.py) ... \u001b[?25ldone\n",
      "\u001b[?25hRequirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from requests->visualdl>=2.2.2->paddlex) (1.25.6)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from requests->visualdl>=2.2.2->paddlex) (2019.9.11)\n",
      "Requirement already satisfied: idna<3,>=2.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from requests->visualdl>=2.2.2->paddlex) (2.8)\n",
      "Requirement already satisfied: typing-extensions>=3.6.4 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from importlib-metadata<4.3->flake8>=3.7.9->visualdl>=2.2.2->paddlex) (4.0.1)\n",
      "Requirement already satisfied: zipp>=0.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from importlib-metadata<4.3->flake8>=3.7.9->visualdl>=2.2.2->paddlex) (3.7.0)\n",
      "Requirement already satisfied: MarkupSafe>=0.23 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from Jinja2>=2.10.1->flask>=1.1.1->visualdl>=2.2.2->paddlex) (2.0.1)\n",
      "Building wheels for collected packages: lap, pycocotools, py-cpuinfo\n",
      "  Building wheel for lap (setup.py) ... \u001b[?25ldone\n",
      "\u001b[?25h  Created wheel for lap: filename=lap-0.4.0-cp37-cp37m-linux_x86_64.whl size=1593872 sha256=2820a204d860f88ae6040dce7c712a9c564121c4d5301919ca405aafa625d938\n",
      "  Stored in directory: /home/aistudio/.cache/pip/wheels/5c/d0/d2/e331d17a999666b1e2eb99743cfa1742629f9d26c55c657001\n",
      "  Building wheel for pycocotools (pyproject.toml) ... \u001b[?25ldone\n",
      "\u001b[?25h  Created wheel for pycocotools: filename=pycocotools-2.0.4-cp37-cp37m-linux_x86_64.whl size=273792 sha256=4a0b73714f4387130408cc984aeb091042c12637eb2d647c1f3c24b752b29aa1\n",
      "  Stored in directory: /home/aistudio/.cache/pip/wheels/c0/01/5f/670dfd20204fc9cc6bf843db4e014acb998f411922e3abc49f\n",
      "  Building wheel for py-cpuinfo (setup.py) ... \u001b[?25ldone\n",
      "\u001b[?25h  Created wheel for py-cpuinfo: filename=py_cpuinfo-8.0.0-py3-none-any.whl size=22245 sha256=b4ef1f6266eaa593cb377e54f084b8cf0b23df7746fb150dec20b4c179c1b169\n",
      "  Stored in directory: /home/aistudio/.cache/pip/wheels/88/c7/d0/6309c7cc9929894c11fe8e516c3e2a0d0a53ee4e198eac48b7\n",
      "Successfully built lap pycocotools py-cpuinfo\n",
      "Installing collected packages: tomli, py, iniconfig, pytest, py-cpuinfo, xmltodict, pytest-benchmark, flake8-import-order, visualdl, shapely, scikit-learn, pycocotools, paddleslim, motmetrics, lap, paddlex\n",
      "  Attempting uninstall: visualdl\n",
      "    Found existing installation: visualdl 2.2.0\n",
      "    Uninstalling visualdl-2.2.0:\n",
      "      Successfully uninstalled visualdl-2.2.0\n",
      "  Attempting uninstall: scikit-learn\n",
      "    Found existing installation: scikit-learn 0.24.2\n",
      "    Uninstalling scikit-learn-0.24.2:\n",
      "      Successfully uninstalled scikit-learn-0.24.2\n",
      "Successfully installed flake8-import-order-0.18.1 iniconfig-1.1.1 lap-0.4.0 motmetrics-1.2.0 paddleslim-2.2.1 paddlex-2.1.0 py-1.11.0 py-cpuinfo-8.0.0 pycocotools-2.0.4 pytest-7.0.1 pytest-benchmark-3.4.1 scikit-learn-0.23.2 shapely-1.8.1.post1 tomli-2.0.1 visualdl-2.2.3 xmltodict-0.12.0\n",
      "\u001b[33mWARNING: You are using pip version 21.3.1; however, version 22.0.3 is available.\n",
      "You should consider upgrading via the '/opt/conda/envs/python35-paddle120-env/bin/python -m pip install --upgrade pip' command.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "# pip安装PaddleX\n",
    "!pip install paddlex"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# PaddleX/paddlex/tools/dataset_split/utils.py\n",
    "\n",
    "# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License.\n",
    "\n",
    "import os\n",
    "import os.path as osp\n",
    "from PIL import Image\n",
    "import numpy as np\n",
    "import json\n",
    "\n",
    "\n",
    "class MyEncoder(json.JSONEncoder):\n",
    "    # 调整json文件存储形式\n",
    "    def default(self, obj):\n",
    "        if isinstance(obj, np.integer):\n",
    "            return int(obj)\n",
    "        elif isinstance(obj, np.floating):\n",
    "            return float(obj)\n",
    "        elif isinstance(obj, np.ndarray):\n",
    "            return obj.tolist()\n",
    "        else:\n",
    "            return super(MyEncoder, self).default(obj)\n",
    "\n",
    "\n",
    "def list_files(dirname):\n",
    "    \"\"\" 列出目录下所有文件（包括所属的一级子目录下文件）\n",
    "\n",
    "    Args:\n",
    "        dirname: 目录路径\n",
    "    \"\"\"\n",
    "\n",
    "    def filter_file(f):\n",
    "        if f.startswith('.'):\n",
    "            return True\n",
    "        return False\n",
    "\n",
    "    all_files = list()\n",
    "    dirs = list()\n",
    "    for f in os.listdir(dirname):\n",
    "        if filter_file(f):\n",
    "            continue\n",
    "        if osp.isdir(osp.join(dirname, f)):\n",
    "            dirs.append(f)\n",
    "        else:\n",
    "            all_files.append(f)\n",
    "    for d in dirs:\n",
    "        for f in os.listdir(osp.join(dirname, d)):\n",
    "            if filter_file(f):\n",
    "                continue\n",
    "            if osp.isdir(osp.join(dirname, d, f)):\n",
    "                continue\n",
    "            all_files.append(osp.join(d, f))\n",
    "    return all_files\n",
    "\n",
    "\n",
    "def is_pic(filename):\n",
    "    \"\"\" 判断文件是否为图片格式\n",
    "\n",
    "    Args:\n",
    "        filename: 文件路径\n",
    "    \"\"\"\n",
    "    suffixes = {'JPEG', 'jpeg', 'JPG', 'jpg', 'BMP', 'bmp', 'PNG', 'png'}\n",
    "    suffix = filename.strip().split('.')[-1]\n",
    "    if suffix not in suffixes:\n",
    "        return False\n",
    "    return True\n",
    "\n",
    "\n",
    "def replace_ext(filename, new_ext):\n",
    "    \"\"\" 替换文件后缀\n",
    "\n",
    "    Args:\n",
    "        filename: 文件路径\n",
    "        new_ext: 需要替换的新的后缀\n",
    "    \"\"\"\n",
    "    items = filename.split(\".\")\n",
    "    items[-1] = new_ext\n",
    "    new_filename = \".\".join(items)\n",
    "    return new_filename\n",
    "\n",
    "\n",
    "def read_seg_ann(pngfile):\n",
    "    \"\"\" 解析语义分割的标注png图片\n",
    "\n",
    "    Args:\n",
    "        pngfile: 包含标注信息的png图片路径\n",
    "    \"\"\"\n",
    "    grt = np.asarray(Image.open(pngfile))\n",
    "    labels = list(np.unique(grt))\n",
    "    if 255 in labels:\n",
    "        labels.remove(255)\n",
    "    return labels\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[02-27 14:28:04 MainThread @utils.py:79] WRN paddlepaddle version: 2.2.2. The dynamic graph version of PARL is under development, not fully tested and supported\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/parl/remote/communication.py:38: DeprecationWarning: 'pyarrow.default_serialization_context' is deprecated as of 2.0.0 and will be removed in a future version. Use pickle or the pyarrow IPC functionality instead.\n",
      "  context = pyarrow.default_serialization_context()\n",
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working\n",
      "  from collections import MutableMapping\n",
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working\n",
      "  from collections import Iterable, Mapping\n",
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working\n",
      "  from collections import Sized\n"
     ]
    }
   ],
   "source": [
    "# PaddleX/paddlex/utils/logging.py\n",
    "\n",
    "# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#    http://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License.\n",
    "\n",
    "import time\n",
    "import os\n",
    "import sys\n",
    "import colorama\n",
    "from colorama import init\n",
    "import paddlex\n",
    "\n",
    "init(autoreset=True)\n",
    "levels = {0: 'ERROR', 1: 'WARNING', 2: 'INFO', 3: 'DEBUG'}\n",
    "\n",
    "\n",
    "def log(level=2, message=\"\", use_color=False):\n",
    "    current_time = time.time()\n",
    "    time_array = time.localtime(current_time)\n",
    "    current_time = time.strftime(\"%Y-%m-%d %H:%M:%S\", time_array)\n",
    "    if paddlex.log_level >= level:\n",
    "        if use_color:\n",
    "            print(\"\\033[1;31;40m{} [{}]\\t{}\\033[0m\".format(\n",
    "                current_time, levels[level], message).encode(\"utf-8\").decode(\n",
    "                    \"latin1\"))\n",
    "        else:\n",
    "            print(\"{} [{}]\\t{}\".format(current_time, levels[level], message)\n",
    "                  .encode(\"utf-8\").decode(\"latin1\"))\n",
    "        sys.stdout.flush()\n",
    "\n",
    "\n",
    "def debug(message=\"\", use_color=False):\n",
    "    log(level=3, message=message, use_color=use_color)\n",
    "\n",
    "\n",
    "def info(message=\"\", use_color=False):\n",
    "    log(level=2, message=message, use_color=use_color)\n",
    "\n",
    "\n",
    "def warning(message=\"\", use_color=True):\n",
    "    log(level=1, message=message, use_color=use_color)\n",
    "\n",
    "\n",
    "def error(message=\"\", use_color=True, exit=True):\n",
    "    log(level=0, message=message, use_color=use_color)\n",
    "    if exit:\n",
    "        sys.exit(-1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License.\n",
    "\n",
    "import os.path as osp\n",
    "import random\n",
    "import xml.etree.ElementTree as ET\n",
    "\n",
    "\n",
    "def split_voc_dataset(dataset_dir, val_percent, test_percent, save_dir):\n",
    "    # 注意图片目录和标注目录名已全部修改\n",
    "    if not osp.exists(osp.join(dataset_dir, \"images\")):\n",
    "        logging.error(\"\\'images\\' is not found in {}!\".format(dataset_dir))\n",
    "    if not osp.exists(osp.join(dataset_dir, \"annotations\")):\n",
    "        logging.error(\"\\'annotations\\' is not found in {}!\".format(\n",
    "            dataset_dir))\n",
    "\n",
    "    all_image_files = list_files(osp.join(dataset_dir, \"images\"))\n",
    "\n",
    "    image_anno_list = list()\n",
    "    label_list = list()\n",
    "    for image_file in all_image_files:\n",
    "        if not is_pic(image_file):\n",
    "            continue\n",
    "        anno_name = replace_ext(image_file, \"xml\")\n",
    "        if osp.exists(osp.join(dataset_dir, \"annotations\", anno_name)):\n",
    "            image_anno_list.append([image_file, anno_name])\n",
    "            try:\n",
    "                tree = ET.parse(\n",
    "                    osp.join(dataset_dir, \"annotations\", anno_name))\n",
    "            except:\n",
    "                raise Exception(\"文件{}不是一个良构的xml文件，请检查标注文件\".format(\n",
    "                    osp.join(dataset_dir, \"annotations\", anno_name)))\n",
    "            objs = tree.findall(\"object\")\n",
    "            for i, obj in enumerate(objs):\n",
    "                cname = obj.find('name').text\n",
    "                if not cname in label_list:\n",
    "                    label_list.append(cname)\n",
    "        else:\n",
    "            logging.error(\"The annotation file {} doesn't exist!\".format(\n",
    "                anno_name))\n",
    "\n",
    "    random.shuffle(image_anno_list)\n",
    "    image_num = len(image_anno_list)\n",
    "    val_num = int(image_num * val_percent)\n",
    "    test_num = int(image_num * test_percent)\n",
    "    train_num = image_num - val_num - test_num\n",
    "\n",
    "    train_image_anno_list = image_anno_list[:train_num]\n",
    "    val_image_anno_list = image_anno_list[train_num:train_num + val_num]\n",
    "    test_image_anno_list = image_anno_list[train_num + val_num:]\n",
    "\n",
    "    with open(\n",
    "            osp.join(save_dir, 'train_list.txt'), mode='w',\n",
    "            encoding='utf-8') as f:\n",
    "        for x in train_image_anno_list:\n",
    "            file = osp.join(\"images\", x[0])\n",
    "            label = osp.join(\"annotations\", x[1])\n",
    "            f.write('{} {}\\n'.format(file, label))\n",
    "    with open(\n",
    "            osp.join(save_dir, 'val_list.txt'), mode='w',\n",
    "            encoding='utf-8') as f:\n",
    "        for x in val_image_anno_list:\n",
    "            file = osp.join(\"images\", x[0])\n",
    "            label = osp.join(\"annotations\", x[1])\n",
    "            f.write('{} {}\\n'.format(file, label))\n",
    "    if len(test_image_anno_list):\n",
    "        with open(\n",
    "                osp.join(save_dir, 'test_list.txt'), mode='w',\n",
    "                encoding='utf-8') as f:\n",
    "            for x in test_image_anno_list:\n",
    "                file = osp.join(\"images\", x[0])\n",
    "                label = osp.join(\"annotations\", x[1])\n",
    "                f.write('{} {}\\n'.format(file, label))\n",
    "    with open(\n",
    "            osp.join(save_dir, 'labels.txt'), mode='w', encoding='utf-8') as f:\n",
    "        for l in sorted(label_list):\n",
    "            f.write('{}\\n'.format(l))\n",
    "\n",
    "    return train_num, val_num, test_num\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # 切分数据集\n",
    "    split_voc_dataset('MyDataset', 0.2, 0.1, 'MyDataset')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# pp-yolo与yolov3_darknet53训练性能比较\n",
    "这里直接使用[官方文档pp-yolo训练代码](https://github.com/PaddlePaddle/PaddleX/blob/develop/tutorials/train/object_detection/ppyolo.py)。\n",
    "\n",
    "VisualDL训练过程请查看`output/ppyolo/vdl_log`目录。\n",
    "\n",
    "1. loss下降趋势\n",
    "\n",
    "<center class = \"half\">\n",
    "<img src=https://ai-studio-static-online.cdn.bcebos.com/5c621c45924d4904ad54b5eef223b59e22cffdc0ce9d46d6827f205109cac385 width=50% align=left><img src=https://ai-studio-static-online.cdn.bcebos.com/19d0b3f2ef7d4a8f9d4707ce1270b50e7b69d9afbd324c949e06ee7808fbe1c2 width=50% align=right>\n",
    "</center>\n",
    "\n",
    "2. 学习率变化\n",
    "\n",
    "<center class = \"half\">\n",
    "<img src=https://ai-studio-static-online.cdn.bcebos.com/959162b0035447698b40b5ef789e60273540310d957548429689cabcbd8a02f0 width=50% align=left><img src=https://ai-studio-static-online.cdn.bcebos.com/da67f4062831452392f50c8b754d340d4fb8b31cc6da47b89094b43d9432de45 width=50% align=right>\n",
    "</center>\n",
    "\n",
    "3. 验证集上bbox_map变化\n",
    "\n",
    "<center class = \"half\">\n",
    "<img src=https://ai-studio-static-online.cdn.bcebos.com/a7201bd8f6d54e49989165bff311acc90a5da1d97acb45e88154659e910e9400 width=50% align=left><img src=https://ai-studio-static-online.cdn.bcebos.com/9fdde2c046394465a973a2dac23aa22377f914a7cf584637a572bf82bf1b166e width=50% align=right>\n",
    "</center>\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 开始训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-27 14:28:08 [INFO]\tStarting to read file list from dataset...\n",
      "2022-02-27 14:28:17 [INFO]\t3500 samples in file MyDataset/train_list.txt, including 3500 positive samples and 0 negative samples.\n",
      "creating index...\n",
      "index created!\n",
      "2022-02-27 14:28:17 [INFO]\tStarting to read file list from dataset...\n",
      "2022-02-27 14:28:20 [INFO]\t1000 samples in file MyDataset/val_list.txt, including 1000 positive samples and 0 negative samples.\n",
      "creating index...\n",
      "index created!\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "W0227 14:28:20.310197   101 device_context.cc:447] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 10.1, Runtime API Version: 10.1\n",
      "W0227 14:28:20.315976   101 device_context.cc:465] device: 0, cuDNN Version: 7.6.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-27 14:28:23 [INFO]\tLoading pretrained model from output/ppyolo/pretrain/ppyolo_r50vd_dcn_2x_coco.pdparams\n",
      "2022-02-27 14:28:24 [WARNING]\t[SKIP] Shape of pretrained params yolo_head.yolo_output.0.weight doesn't match.(Pretrained: (258, 1024, 1, 1), Actual: [27, 1024, 1, 1])\n",
      "2022-02-27 14:28:24 [WARNING]\t[SKIP] Shape of pretrained params yolo_head.yolo_output.0.bias doesn't match.(Pretrained: (258,), Actual: [27])\n",
      "2022-02-27 14:28:24 [WARNING]\t[SKIP] Shape of pretrained params yolo_head.yolo_output.1.weight doesn't match.(Pretrained: (258, 512, 1, 1), Actual: [27, 512, 1, 1])\n",
      "2022-02-27 14:28:24 [WARNING]\t[SKIP] Shape of pretrained params yolo_head.yolo_output.1.bias doesn't match.(Pretrained: (258,), Actual: [27])\n",
      "2022-02-27 14:28:24 [WARNING]\t[SKIP] Shape of pretrained params yolo_head.yolo_output.2.weight doesn't match.(Pretrained: (258, 256, 1, 1), Actual: [27, 256, 1, 1])\n",
      "2022-02-27 14:28:24 [WARNING]\t[SKIP] Shape of pretrained params yolo_head.yolo_output.2.bias doesn't match.(Pretrained: (258,), Actual: [27])\n",
      "2022-02-27 14:28:24 [INFO]\tThere are 386/392 variables loaded into PPYOLO.\n",
      "2022-02-27 14:28:29 [INFO]\t[TRAIN] Epoch=1/2, Step=10/437, loss_xy=4.020419, loss_wh=9.301065, loss_iou=17.116806, loss_iou_aware=2.902114, loss_obj=368.031860, loss_cls=8.346281, loss=409.718536, lr=0.000008, time_each_step=0.42s, eta=0:6:59\n",
      "2022-02-27 14:28:32 [INFO]\t[TRAIN] Epoch=1/2, Step=20/437, loss_xy=5.551459, loss_wh=8.965790, loss_iou=19.958326, loss_iou_aware=3.694800, loss_obj=119.717438, loss_cls=10.839416, loss=168.727234, lr=0.000016, time_each_step=0.35s, eta=0:5:40\n",
      "2022-02-27 14:28:35 [INFO]\t[TRAIN] Epoch=1/2, Step=30/437, loss_xy=8.027088, loss_wh=12.860194, loss_iou=28.697935, loss_iou_aware=5.258214, loss_obj=103.694824, loss_cls=13.875370, loss=172.413620, lr=0.000024, time_each_step=0.29s, eta=0:4:39\n",
      "2022-02-27 14:28:38 [INFO]\t[TRAIN] Epoch=1/2, Step=40/437, loss_xy=5.690256, loss_wh=6.522244, loss_iou=17.847050, loss_iou_aware=3.395849, loss_obj=39.530174, loss_cls=10.436437, loss=83.422012, lr=0.000033, time_each_step=0.27s, eta=0:4:23\n",
      "2022-02-27 14:28:41 [INFO]\t[TRAIN] Epoch=1/2, Step=50/437, loss_xy=5.632270, loss_wh=6.388397, loss_iou=20.011566, loss_iou_aware=3.493344, loss_obj=47.802113, loss_cls=9.103252, loss=92.430939, lr=0.000041, time_each_step=0.3s, eta=0:4:46\n",
      "2022-02-27 14:28:44 [INFO]\t[TRAIN] Epoch=1/2, Step=60/437, loss_xy=5.845295, loss_wh=12.210073, loss_iou=27.009081, loss_iou_aware=4.442380, loss_obj=36.819725, loss_cls=12.538715, loss=98.865265, lr=0.000049, time_each_step=0.31s, eta=0:4:55\n",
      "2022-02-27 14:28:47 [INFO]\t[TRAIN] Epoch=1/2, Step=70/437, loss_xy=5.786249, loss_wh=6.300038, loss_iou=18.523849, loss_iou_aware=3.850284, loss_obj=37.705700, loss_cls=11.037926, loss=83.204048, lr=0.000058, time_each_step=0.31s, eta=0:4:50\n",
      "2022-02-27 14:28:51 [INFO]\t[TRAIN] Epoch=1/2, Step=80/437, loss_xy=4.796140, loss_wh=5.460245, loss_iou=18.216805, loss_iou_aware=3.791192, loss_obj=33.351788, loss_cls=10.308134, loss=75.924301, lr=0.000066, time_each_step=0.32s, eta=0:4:51\n",
      "2022-02-27 14:28:53 [INFO]\t[TRAIN] Epoch=1/2, Step=90/437, loss_xy=3.485981, loss_wh=5.450521, loss_iou=13.823080, loss_iou_aware=2.513620, loss_obj=18.227516, loss_cls=6.447694, loss=49.948410, lr=0.000074, time_each_step=0.27s, eta=0:4:9\n",
      "2022-02-27 14:28:57 [INFO]\t[TRAIN] Epoch=1/2, Step=100/437, loss_xy=4.596140, loss_wh=4.590405, loss_iou=15.530546, loss_iou_aware=3.406658, loss_obj=25.254044, loss_cls=8.221707, loss=61.599499, lr=0.000082, time_each_step=0.32s, eta=0:4:51\n",
      "2022-02-27 14:28:59 [INFO]\t[TRAIN] Epoch=1/2, Step=110/437, loss_xy=5.329105, loss_wh=6.246416, loss_iou=18.113804, loss_iou_aware=3.594350, loss_obj=25.347538, loss_cls=8.153648, loss=66.784866, lr=0.000091, time_each_step=0.29s, eta=0:4:17\n",
      "2022-02-27 14:29:03 [INFO]\t[TRAIN] Epoch=1/2, Step=120/437, loss_xy=3.374919, loss_wh=3.339671, loss_iou=10.914745, loss_iou_aware=2.410643, loss_obj=17.300217, loss_cls=5.601151, loss=42.941345, lr=0.000099, time_each_step=0.31s, eta=0:4:36\n",
      "2022-02-27 14:29:05 [INFO]\t[TRAIN] Epoch=1/2, Step=130/437, loss_xy=3.013599, loss_wh=2.405508, loss_iou=10.310445, loss_iou_aware=2.244553, loss_obj=15.747811, loss_cls=4.541445, loss=38.263359, lr=0.000108, time_each_step=0.26s, eta=0:3:47\n",
      "2022-02-27 14:29:09 [INFO]\t[TRAIN] Epoch=1/2, Step=140/437, loss_xy=4.443967, loss_wh=3.946382, loss_iou=15.301961, loss_iou_aware=3.274268, loss_obj=20.635765, loss_cls=7.956191, loss=55.558533, lr=0.000116, time_each_step=0.36s, eta=0:5:5\n",
      "2022-02-27 14:29:12 [INFO]\t[TRAIN] Epoch=1/2, Step=150/437, loss_xy=3.876187, loss_wh=2.951611, loss_iou=11.593500, loss_iou_aware=2.822318, loss_obj=24.775900, loss_cls=6.052805, loss=52.072319, lr=0.000124, time_each_step=0.33s, eta=0:4:39\n",
      "2022-02-27 14:29:15 [INFO]\t[TRAIN] Epoch=1/2, Step=160/437, loss_xy=4.644635, loss_wh=3.081737, loss_iou=13.691435, loss_iou_aware=3.189936, loss_obj=21.914532, loss_cls=6.107724, loss=52.629997, lr=0.000132, time_each_step=0.29s, eta=0:4:4\n",
      "2022-02-27 14:29:18 [INFO]\t[TRAIN] Epoch=1/2, Step=170/437, loss_xy=4.093678, loss_wh=3.780703, loss_iou=15.818883, loss_iou_aware=3.300193, loss_obj=19.897337, loss_cls=6.879167, loss=53.769958, lr=0.000141, time_each_step=0.28s, eta=0:3:55\n",
      "2022-02-27 14:29:21 [INFO]\t[TRAIN] Epoch=1/2, Step=180/437, loss_xy=3.086883, loss_wh=3.279399, loss_iou=12.469090, loss_iou_aware=2.462240, loss_obj=14.981279, loss_cls=3.819179, loss=40.098072, lr=0.000149, time_each_step=0.3s, eta=0:4:7\n",
      "2022-02-27 14:29:23 [INFO]\t[TRAIN] Epoch=1/2, Step=190/437, loss_xy=3.858347, loss_wh=4.496908, loss_iou=15.664784, loss_iou_aware=3.289910, loss_obj=18.414917, loss_cls=4.315392, loss=50.040257, lr=0.000158, time_each_step=0.27s, eta=0:3:34\n",
      "2022-02-27 14:29:27 [INFO]\t[TRAIN] Epoch=1/2, Step=200/437, loss_xy=7.229821, loss_wh=5.500059, loss_iou=21.815464, loss_iou_aware=5.268685, loss_obj=34.958267, loss_cls=14.772333, loss=89.544624, lr=0.000166, time_each_step=0.34s, eta=0:4:30\n",
      "2022-02-27 14:29:29 [INFO]\t[TRAIN] Epoch=1/2, Step=210/437, loss_xy=4.477809, loss_wh=4.317667, loss_iou=18.200703, loss_iou_aware=3.986412, loss_obj=17.330469, loss_cls=6.468187, loss=54.781242, lr=0.000174, time_each_step=0.26s, eta=0:3:26\n",
      "2022-02-27 14:29:33 [INFO]\t[TRAIN] Epoch=1/2, Step=220/437, loss_xy=3.338529, loss_wh=2.485778, loss_iou=9.972981, loss_iou_aware=2.580505, loss_obj=19.513721, loss_cls=4.498016, loss=42.389534, lr=0.000183, time_each_step=0.31s, eta=0:3:59\n",
      "2022-02-27 14:29:35 [INFO]\t[TRAIN] Epoch=1/2, Step=230/437, loss_xy=7.318787, loss_wh=6.353737, loss_iou=24.610823, loss_iou_aware=5.597968, loss_obj=36.880577, loss_cls=10.842798, loss=91.604691, lr=0.000191, time_each_step=0.26s, eta=0:3:20\n",
      "2022-02-27 14:29:38 [INFO]\t[TRAIN] Epoch=1/2, Step=240/437, loss_xy=2.559197, loss_wh=2.663649, loss_iou=9.744902, loss_iou_aware=2.334237, loss_obj=15.102444, loss_cls=3.331882, loss=35.736309, lr=0.000199, time_each_step=0.29s, eta=0:3:38\n",
      "2022-02-27 14:29:41 [INFO]\t[TRAIN] Epoch=1/2, Step=250/437, loss_xy=8.483913, loss_wh=9.735029, loss_iou=35.497906, loss_iou_aware=7.585621, loss_obj=39.792152, loss_cls=13.624226, loss=114.718842, lr=0.000208, time_each_step=0.27s, eta=0:3:25\n",
      "2022-02-27 14:29:44 [INFO]\t[TRAIN] Epoch=1/2, Step=260/437, loss_xy=2.248137, loss_wh=2.312220, loss_iou=8.395769, loss_iou_aware=1.967950, loss_obj=12.999285, loss_cls=2.956087, loss=30.879450, lr=0.000216, time_each_step=0.3s, eta=0:3:42\n",
      "2022-02-27 14:29:47 [INFO]\t[TRAIN] Epoch=1/2, Step=270/437, loss_xy=3.558502, loss_wh=4.669929, loss_iou=15.355323, loss_iou_aware=3.752039, loss_obj=21.640579, loss_cls=9.211609, loss=58.187981, lr=0.000224, time_each_step=0.27s, eta=0:3:18\n",
      "2022-02-27 14:29:49 [INFO]\t[TRAIN] Epoch=1/2, Step=280/437, loss_xy=3.130790, loss_wh=3.044011, loss_iou=13.322287, loss_iou_aware=3.167615, loss_obj=17.375954, loss_cls=6.111196, loss=46.151852, lr=0.000233, time_each_step=0.3s, eta=0:3:34\n",
      "2022-02-27 14:29:52 [INFO]\t[TRAIN] Epoch=1/2, Step=290/437, loss_xy=3.352833, loss_wh=4.107790, loss_iou=15.668060, loss_iou_aware=3.169514, loss_obj=18.816454, loss_cls=3.883665, loss=48.998314, lr=0.000241, time_each_step=0.29s, eta=0:3:26\n",
      "2022-02-27 14:29:55 [INFO]\t[TRAIN] Epoch=1/2, Step=300/437, loss_xy=2.670184, loss_wh=3.493360, loss_iou=11.800671, loss_iou_aware=2.690745, loss_obj=15.385637, loss_cls=3.718917, loss=39.759514, lr=0.000249, time_each_step=0.3s, eta=0:3:30\n",
      "2022-02-27 14:29:58 [INFO]\t[TRAIN] Epoch=1/2, Step=310/437, loss_xy=2.426150, loss_wh=2.558167, loss_iou=9.165842, loss_iou_aware=2.102141, loss_obj=12.388887, loss_cls=2.178740, loss=30.819927, lr=0.000258, time_each_step=0.31s, eta=0:3:30\n",
      "2022-02-27 14:30:01 [INFO]\t[TRAIN] Epoch=1/2, Step=320/437, loss_xy=4.565819, loss_wh=5.242090, loss_iou=21.336000, loss_iou_aware=4.840374, loss_obj=24.041245, loss_cls=9.983552, loss=70.009079, lr=0.000266, time_each_step=0.28s, eta=0:3:10\n",
      "2022-02-27 14:30:05 [INFO]\t[TRAIN] Epoch=1/2, Step=330/437, loss_xy=3.505461, loss_wh=3.500977, loss_iou=13.482828, loss_iou_aware=3.142982, loss_obj=20.041683, loss_cls=4.716985, loss=48.390915, lr=0.000274, time_each_step=0.32s, eta=0:3:35\n",
      "2022-02-27 14:30:07 [INFO]\t[TRAIN] Epoch=1/2, Step=340/437, loss_xy=2.915398, loss_wh=3.681272, loss_iou=12.851963, loss_iou_aware=3.082778, loss_obj=18.788958, loss_cls=7.729738, loss=49.050102, lr=0.000283, time_each_step=0.28s, eta=0:3:3\n",
      "2022-02-27 14:30:10 [INFO]\t[TRAIN] Epoch=1/2, Step=350/437, loss_xy=2.465625, loss_wh=2.311618, loss_iou=9.092141, loss_iou_aware=1.977345, loss_obj=13.444095, loss_cls=3.868860, loss=33.159683, lr=0.000291, time_each_step=0.29s, eta=0:3:9\n",
      "2022-02-27 14:30:13 [INFO]\t[TRAIN] Epoch=1/2, Step=360/437, loss_xy=1.822560, loss_wh=2.257788, loss_iou=8.580810, loss_iou_aware=1.925733, loss_obj=10.505983, loss_cls=3.995856, loss=29.088730, lr=0.000299, time_each_step=0.27s, eta=0:2:53\n",
      "2022-02-27 14:30:16 [INFO]\t[TRAIN] Epoch=1/2, Step=370/437, loss_xy=3.265904, loss_wh=3.409080, loss_iou=12.782509, loss_iou_aware=3.184885, loss_obj=21.191641, loss_cls=7.592350, loss=51.426369, lr=0.000307, time_each_step=0.28s, eta=0:2:56\n",
      "2022-02-27 14:30:18 [INFO]\t[TRAIN] Epoch=1/2, Step=380/437, loss_xy=2.018880, loss_wh=2.526059, loss_iou=8.960682, loss_iou_aware=2.148219, loss_obj=13.629147, loss_cls=2.396394, loss=31.679379, lr=0.000316, time_each_step=0.27s, eta=0:2:46\n",
      "2022-02-27 14:30:21 [INFO]\t[TRAIN] Epoch=1/2, Step=390/437, loss_xy=2.475314, loss_wh=3.676172, loss_iou=13.238057, loss_iou_aware=3.147604, loss_obj=14.591438, loss_cls=5.743556, loss=42.872143, lr=0.000324, time_each_step=0.29s, eta=0:2:57\n",
      "2022-02-27 14:30:24 [INFO]\t[TRAIN] Epoch=1/2, Step=400/437, loss_xy=3.940691, loss_wh=4.298354, loss_iou=16.329868, loss_iou_aware=3.949655, loss_obj=23.861740, loss_cls=5.447444, loss=57.827755, lr=0.000333, time_each_step=0.27s, eta=0:2:39\n",
      "2022-02-27 14:30:27 [INFO]\t[TRAIN] Epoch=1/2, Step=410/437, loss_xy=2.368795, loss_wh=2.819495, loss_iou=10.949609, loss_iou_aware=2.687283, loss_obj=17.227045, loss_cls=5.253524, loss=41.305752, lr=0.000341, time_each_step=0.28s, eta=0:2:42\n",
      "2022-02-27 14:30:30 [INFO]\t[TRAIN] Epoch=1/2, Step=420/437, loss_xy=2.276117, loss_wh=3.604119, loss_iou=11.499086, loss_iou_aware=2.620481, loss_obj=15.275774, loss_cls=5.010044, loss=40.285622, lr=0.000349, time_each_step=0.28s, eta=0:2:43\n",
      "2022-02-27 14:30:32 [INFO]\t[TRAIN] Epoch=1/2, Step=430/437, loss_xy=2.852012, loss_wh=3.182958, loss_iou=13.055834, loss_iou_aware=3.063055, loss_obj=14.833294, loss_cls=3.149016, loss=40.136169, lr=0.000358, time_each_step=0.28s, eta=0:2:38\n",
      "2022-02-27 14:30:34 [INFO]\t[TRAIN] Epoch 1 finished, loss_xy=3.9590855, loss_wh=4.7932453, loss_iou=15.425473, loss_iou_aware=3.312508, loss_obj=173.26718, loss_cls=6.9460144, loss=207.70352 .\n",
      "2022-02-27 14:30:36 [INFO]\t[TRAIN] Epoch=2/2, Step=3/437, loss_xy=2.422137, loss_wh=2.652185, loss_iou=10.274245, loss_iou_aware=2.295620, loss_obj=14.845804, loss_cls=2.494484, loss=34.984474, lr=0.000366, time_each_step=0.33s, eta=0:3:5\n",
      "2022-02-27 14:30:39 [INFO]\t[TRAIN] Epoch=2/2, Step=13/437, loss_xy=2.948032, loss_wh=3.354394, loss_iou=12.911718, loss_iou_aware=3.113269, loss_obj=17.699167, loss_cls=6.545652, loss=46.572235, lr=0.000374, time_each_step=0.29s, eta=0:2:39\n",
      "2022-02-27 14:30:41 [INFO]\t[TRAIN] Epoch=2/2, Step=23/437, loss_xy=2.496958, loss_wh=3.651025, loss_iou=13.277812, loss_iou_aware=3.074347, loss_obj=20.270226, loss_cls=5.143090, loss=47.913456, lr=0.000383, time_each_step=0.26s, eta=0:2:21\n",
      "2022-02-27 14:30:45 [INFO]\t[TRAIN] Epoch=2/2, Step=33/437, loss_xy=3.173711, loss_wh=4.463820, loss_iou=14.566559, loss_iou_aware=3.254825, loss_obj=16.224140, loss_cls=6.574031, loss=48.257088, lr=0.000391, time_each_step=0.33s, eta=0:2:52\n",
      "2022-02-27 14:30:47 [INFO]\t[TRAIN] Epoch=2/2, Step=43/437, loss_xy=3.227288, loss_wh=3.409949, loss_iou=12.593338, loss_iou_aware=2.849216, loss_obj=16.630344, loss_cls=3.563415, loss=42.273552, lr=0.000399, time_each_step=0.28s, eta=0:2:26\n",
      "2022-02-27 14:30:51 [INFO]\t[TRAIN] Epoch=2/2, Step=53/437, loss_xy=6.219450, loss_wh=8.591665, loss_iou=29.669258, loss_iou_aware=6.843362, loss_obj=29.187864, loss_cls=16.650066, loss=97.161667, lr=0.000408, time_each_step=0.31s, eta=0:2:39\n",
      "2022-02-27 14:30:53 [INFO]\t[TRAIN] Epoch=2/2, Step=63/437, loss_xy=3.204665, loss_wh=4.631492, loss_iou=16.500784, loss_iou_aware=3.320472, loss_obj=14.623079, loss_cls=5.999361, loss=48.279854, lr=0.000416, time_each_step=0.28s, eta=0:2:18\n",
      "2022-02-27 14:30:57 [INFO]\t[TRAIN] Epoch=2/2, Step=73/437, loss_xy=4.252413, loss_wh=5.891181, loss_iou=20.134769, loss_iou_aware=4.297791, loss_obj=20.681097, loss_cls=5.968986, loss=61.226234, lr=0.000417, time_each_step=0.32s, eta=0:2:34\n",
      "2022-02-27 14:30:59 [INFO]\t[TRAIN] Epoch=2/2, Step=83/437, loss_xy=3.523506, loss_wh=3.747663, loss_iou=14.872866, loss_iou_aware=3.612707, loss_obj=21.614040, loss_cls=4.837080, loss=52.207863, lr=0.000417, time_each_step=0.26s, eta=0:2:2\n",
      "2022-02-27 14:31:02 [INFO]\t[TRAIN] Epoch=2/2, Step=93/437, loss_xy=3.204715, loss_wh=3.348552, loss_iou=15.366735, loss_iou_aware=3.583839, loss_obj=18.007261, loss_cls=5.703015, loss=49.214115, lr=0.000417, time_each_step=0.3s, eta=0:2:22\n",
      "2022-02-27 14:31:05 [INFO]\t[TRAIN] Epoch=2/2, Step=103/437, loss_xy=5.335019, loss_wh=6.154953, loss_iou=23.163843, loss_iou_aware=5.212636, loss_obj=31.452152, loss_cls=10.190569, loss=81.509171, lr=0.000417, time_each_step=0.29s, eta=0:2:13\n",
      "2022-02-27 14:31:08 [INFO]\t[TRAIN] Epoch=2/2, Step=113/437, loss_xy=3.136352, loss_wh=4.649093, loss_iou=17.197287, loss_iou_aware=3.811260, loss_obj=18.862129, loss_cls=6.134809, loss=53.790932, lr=0.000417, time_each_step=0.26s, eta=0:1:57\n",
      "2022-02-27 14:31:10 [INFO]\t[TRAIN] Epoch=2/2, Step=123/437, loss_xy=2.638861, loss_wh=3.288607, loss_iou=12.826881, loss_iou_aware=3.015700, loss_obj=17.578276, loss_cls=3.958315, loss=43.306641, lr=0.000417, time_each_step=0.27s, eta=0:1:58\n",
      "2022-02-27 14:31:13 [INFO]\t[TRAIN] Epoch=2/2, Step=133/437, loss_xy=3.829835, loss_wh=4.594249, loss_iou=16.510633, loss_iou_aware=3.893011, loss_obj=19.280983, loss_cls=6.965978, loss=55.074688, lr=0.000417, time_each_step=0.27s, eta=0:1:57\n",
      "2022-02-27 14:31:16 [INFO]\t[TRAIN] Epoch=2/2, Step=143/437, loss_xy=2.078932, loss_wh=2.073185, loss_iou=9.920411, loss_iou_aware=2.461793, loss_obj=13.017055, loss_cls=3.735308, loss=33.286686, lr=0.000417, time_each_step=0.32s, eta=0:2:15\n",
      "2022-02-27 14:31:19 [INFO]\t[TRAIN] Epoch=2/2, Step=153/437, loss_xy=3.663226, loss_wh=4.846887, loss_iou=17.792933, loss_iou_aware=4.181213, loss_obj=21.598503, loss_cls=7.047032, loss=59.129795, lr=0.000417, time_each_step=0.26s, eta=0:1:48\n",
      "2022-02-27 14:31:22 [INFO]\t[TRAIN] Epoch=2/2, Step=163/437, loss_xy=2.680852, loss_wh=3.204486, loss_iou=12.803935, loss_iou_aware=3.203741, loss_obj=19.917763, loss_cls=5.361905, loss=47.172680, lr=0.000417, time_each_step=0.29s, eta=0:1:57\n",
      "2022-02-27 14:31:25 [INFO]\t[TRAIN] Epoch=2/2, Step=173/437, loss_xy=2.479180, loss_wh=2.871918, loss_iou=11.854375, loss_iou_aware=2.813314, loss_obj=15.119920, loss_cls=4.496898, loss=39.635605, lr=0.000417, time_each_step=0.33s, eta=0:2:7\n",
      "2022-02-27 14:31:28 [INFO]\t[TRAIN] Epoch=2/2, Step=183/437, loss_xy=2.249289, loss_wh=3.035949, loss_iou=10.863028, loss_iou_aware=2.526792, loss_obj=12.710980, loss_cls=2.707847, loss=34.093887, lr=0.000417, time_each_step=0.26s, eta=0:1:37\n",
      "2022-02-27 14:31:31 [INFO]\t[TRAIN] Epoch=2/2, Step=193/437, loss_xy=4.492470, loss_wh=6.721217, loss_iou=20.440599, loss_iou_aware=4.371641, loss_obj=24.184889, loss_cls=6.103571, loss=66.314384, lr=0.000417, time_each_step=0.29s, eta=0:1:46\n",
      "2022-02-27 14:31:33 [INFO]\t[TRAIN] Epoch=2/2, Step=203/437, loss_xy=2.830347, loss_wh=3.407024, loss_iou=13.205451, loss_iou_aware=3.305741, loss_obj=16.991772, loss_cls=5.511063, loss=45.251396, lr=0.000417, time_each_step=0.28s, eta=0:1:41\n",
      "2022-02-27 14:31:36 [INFO]\t[TRAIN] Epoch=2/2, Step=213/437, loss_xy=2.774943, loss_wh=3.555378, loss_iou=12.447384, loss_iou_aware=2.944548, loss_obj=16.956657, loss_cls=3.758518, loss=42.437428, lr=0.000417, time_each_step=0.29s, eta=0:1:40\n",
      "2022-02-27 14:31:40 [INFO]\t[TRAIN] Epoch=2/2, Step=223/437, loss_xy=3.088453, loss_wh=4.430357, loss_iou=14.534947, loss_iou_aware=2.973015, loss_obj=16.818674, loss_cls=2.784447, loss=44.629890, lr=0.000417, time_each_step=0.32s, eta=0:1:47\n",
      "2022-02-27 14:31:43 [INFO]\t[TRAIN] Epoch=2/2, Step=233/437, loss_xy=2.931904, loss_wh=3.772831, loss_iou=14.941144, loss_iou_aware=3.314770, loss_obj=20.355070, loss_cls=3.867544, loss=49.183262, lr=0.000417, time_each_step=0.31s, eta=0:1:40\n",
      "2022-02-27 14:31:45 [INFO]\t[TRAIN] Epoch=2/2, Step=243/437, loss_xy=1.897308, loss_wh=1.800226, loss_iou=7.548315, loss_iou_aware=1.955195, loss_obj=13.307528, loss_cls=2.662925, loss=29.171497, lr=0.000417, time_each_step=0.27s, eta=0:1:25\n",
      "2022-02-27 14:31:48 [INFO]\t[TRAIN] Epoch=2/2, Step=253/437, loss_xy=3.021630, loss_wh=2.859684, loss_iou=11.348379, loss_iou_aware=2.929476, loss_obj=17.243372, loss_cls=3.993055, loss=41.395596, lr=0.000417, time_each_step=0.28s, eta=0:1:27\n",
      "2022-02-27 14:31:51 [INFO]\t[TRAIN] Epoch=2/2, Step=263/437, loss_xy=3.848293, loss_wh=4.550056, loss_iou=17.957781, loss_iou_aware=4.114298, loss_obj=23.531130, loss_cls=5.787740, loss=59.789295, lr=0.000417, time_each_step=0.29s, eta=0:1:25\n",
      "2022-02-27 14:31:54 [INFO]\t[TRAIN] Epoch=2/2, Step=273/437, loss_xy=2.050618, loss_wh=2.625304, loss_iou=9.631926, loss_iou_aware=2.371382, loss_obj=12.963955, loss_cls=2.591170, loss=32.234356, lr=0.000417, time_each_step=0.3s, eta=0:1:27\n",
      "2022-02-27 14:31:57 [INFO]\t[TRAIN] Epoch=2/2, Step=283/437, loss_xy=1.780574, loss_wh=2.645116, loss_iou=8.895831, loss_iou_aware=2.083916, loss_obj=14.403099, loss_cls=5.108167, loss=34.916702, lr=0.000417, time_each_step=0.31s, eta=0:1:25\n",
      "2022-02-27 14:32:00 [INFO]\t[TRAIN] Epoch=2/2, Step=293/437, loss_xy=1.904945, loss_wh=2.936574, loss_iou=10.714531, loss_iou_aware=2.551773, loss_obj=11.638516, loss_cls=2.316350, loss=32.062687, lr=0.000417, time_each_step=0.26s, eta=0:1:9\n",
      "2022-02-27 14:32:02 [INFO]\t[TRAIN] Epoch=2/2, Step=303/437, loss_xy=1.808495, loss_wh=2.928469, loss_iou=9.785870, loss_iou_aware=2.135282, loss_obj=9.740849, loss_cls=1.637196, loss=28.036160, lr=0.000417, time_each_step=0.28s, eta=0:1:12\n",
      "2022-02-27 14:32:06 [INFO]\t[TRAIN] Epoch=2/2, Step=313/437, loss_xy=2.009425, loss_wh=2.507953, loss_iou=9.570983, loss_iou_aware=2.367475, loss_obj=16.758179, loss_cls=3.034959, loss=36.248970, lr=0.000417, time_each_step=0.35s, eta=0:1:28\n",
      "2022-02-27 14:32:09 [INFO]\t[TRAIN] Epoch=2/2, Step=323/437, loss_xy=2.354603, loss_wh=4.548288, loss_iou=13.636941, loss_iou_aware=2.597181, loss_obj=13.588432, loss_cls=3.261311, loss=39.986755, lr=0.000417, time_each_step=0.3s, eta=0:1:12\n",
      "2022-02-27 14:32:12 [INFO]\t[TRAIN] Epoch=2/2, Step=333/437, loss_xy=2.283262, loss_wh=3.441410, loss_iou=12.378183, loss_iou_aware=2.841221, loss_obj=18.111111, loss_cls=2.641264, loss=41.696453, lr=0.000417, time_each_step=0.28s, eta=0:1:3\n",
      "2022-02-27 14:32:15 [INFO]\t[TRAIN] Epoch=2/2, Step=343/437, loss_xy=2.213376, loss_wh=3.457772, loss_iou=11.813599, loss_iou_aware=2.727453, loss_obj=12.469112, loss_cls=3.525520, loss=36.206833, lr=0.000417, time_each_step=0.29s, eta=0:1:3\n",
      "2022-02-27 14:32:17 [INFO]\t[TRAIN] Epoch=2/2, Step=353/437, loss_xy=2.851173, loss_wh=4.382623, loss_iou=13.792843, loss_iou_aware=2.924742, loss_obj=14.882047, loss_cls=3.335922, loss=42.169350, lr=0.000417, time_each_step=0.26s, eta=0:0:55\n",
      "2022-02-27 14:32:20 [INFO]\t[TRAIN] Epoch=2/2, Step=363/437, loss_xy=2.149923, loss_wh=2.459264, loss_iou=9.164628, loss_iou_aware=2.350900, loss_obj=14.512928, loss_cls=2.245271, loss=32.882912, lr=0.000417, time_each_step=0.31s, eta=0:1:2\n",
      "2022-02-27 14:32:23 [INFO]\t[TRAIN] Epoch=2/2, Step=373/437, loss_xy=3.648106, loss_wh=4.529582, loss_iou=17.812077, loss_iou_aware=4.305174, loss_obj=20.528206, loss_cls=7.545096, loss=58.368240, lr=0.000417, time_each_step=0.3s, eta=0:0:57\n",
      "2022-02-27 14:32:27 [INFO]\t[TRAIN] Epoch=2/2, Step=383/437, loss_xy=1.729521, loss_wh=2.112310, loss_iou=8.647922, loss_iou_aware=2.239855, loss_obj=9.966718, loss_cls=1.697746, loss=26.394073, lr=0.000417, time_each_step=0.31s, eta=0:0:54\n",
      "2022-02-27 14:32:29 [INFO]\t[TRAIN] Epoch=2/2, Step=393/437, loss_xy=2.584434, loss_wh=2.933686, loss_iou=12.818707, loss_iou_aware=2.918897, loss_obj=14.219541, loss_cls=2.331026, loss=37.806290, lr=0.000417, time_each_step=0.29s, eta=0:0:48\n",
      "2022-02-27 14:32:32 [INFO]\t[TRAIN] Epoch=2/2, Step=403/437, loss_xy=2.107489, loss_wh=3.328546, loss_iou=12.641789, loss_iou_aware=2.961687, loss_obj=15.757040, loss_cls=4.855035, loss=41.651585, lr=0.000417, time_each_step=0.26s, eta=0:0:40\n",
      "2022-02-27 14:32:35 [INFO]\t[TRAIN] Epoch=2/2, Step=413/437, loss_xy=2.795920, loss_wh=3.311296, loss_iou=13.518226, loss_iou_aware=3.173294, loss_obj=18.458549, loss_cls=4.088928, loss=45.346214, lr=0.000417, time_each_step=0.35s, eta=0:0:51\n",
      "2022-02-27 14:32:38 [INFO]\t[TRAIN] Epoch=2/2, Step=423/437, loss_xy=2.786905, loss_wh=3.443718, loss_iou=12.438623, loss_iou_aware=2.988496, loss_obj=19.082539, loss_cls=3.936152, loss=44.676434, lr=0.000417, time_each_step=0.3s, eta=0:0:41\n",
      "2022-02-27 14:32:41 [INFO]\t[TRAIN] Epoch=2/2, Step=433/437, loss_xy=4.893564, loss_wh=4.932778, loss_iou=17.751175, loss_iou_aware=4.447976, loss_obj=29.887165, loss_cls=11.617718, loss=73.530380, lr=0.000417, time_each_step=0.28s, eta=0:0:35\n",
      "2022-02-27 14:32:43 [INFO]\t[TRAIN] Epoch 2 finished, loss_xy=2.7646456, loss_wh=3.5653257, loss_iou=13.111939, loss_iou_aware=3.0817552, loss_obj=16.630365, loss_cls=4.3253055, loss=43.479332 .\n",
      "2022-02-27 14:32:43 [WARNING]\tDetector only supports single card evaluation with batch_size=1 during evaluation, so batch_size is forcibly set to 1.\n",
      "2022-02-27 14:32:43 [INFO]\tStart to evaluate(total_samples=1000, total_steps=1000)...\n",
      "2022-02-27 14:33:34 [INFO]\tAccumulating evaluatation results...\n",
      "2022-02-27 14:33:34 [INFO]\t[EVAL] Finished, Epoch=2, bbox_map=51.641273 .\n",
      "2022-02-27 14:33:36 [INFO]\tModel saved in output/ppyolo/best_model.\n",
      "2022-02-27 14:33:36 [INFO]\tCurrent evaluated best model on eval_dataset is epoch_2, bbox_map=51.64127275969489\n",
      "2022-02-27 14:33:38 [INFO]\tModel saved in output/ppyolo/epoch_2.\n"
     ]
    }
   ],
   "source": [
    "# 环境变量配置，用于控制是否使用GPU\n",
    "# 说明文档：https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html#gpu\n",
    "import os\n",
    "os.environ['CUDA_VISIBLE_DEVICES'] = '0'\n",
    "\n",
    "# from paddlex.det import transforms\n",
    "import paddlex as pdx\n",
    "from paddlex import transforms as T\n",
    "# 定义训练和验证时的transforms\n",
    "# API说明 https://paddlex.readthedocs.io/zh_CN/develop/apis/transforms/det_transforms.html\n",
    "# train_transforms = transforms.Compose([\n",
    "#     transforms.MixupImage(mixup_epoch=250), transforms.RandomDistort(),\n",
    "#     transforms.RandomExpand(), transforms.RandomCrop(), transforms.Resize(\n",
    "#         target_size=608, interp='RANDOM'), transforms.RandomHorizontalFlip(),\n",
    "#     transforms.Normalize()\n",
    "# ])\n",
    "\n",
    "# eval_transforms = transforms.Compose([\n",
    "#     transforms.Resize(\n",
    "#         target_size=608, interp='CUBIC'), transforms.Normalize()\n",
    "# ])\n",
    "train_transforms = T.Compose([\n",
    "    T.MixupImage(mixup_epoch=250), T.RandomDistort(),\n",
    "    T.RandomExpand(im_padding_value=[123.675, 116.28, 103.53]), T.RandomCrop(),\n",
    "    T.RandomHorizontalFlip(), T.BatchRandomResize(\n",
    "        target_sizes=[320, 352, 384, 416, 448, 480, 512, 544, 576, 608],\n",
    "        interp='RANDOM'), T.Normalize(\n",
    "            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "])\n",
    "\n",
    "eval_transforms = T.Compose([\n",
    "    T.Resize(\n",
    "        target_size=608, interp='CUBIC'), T.Normalize(\n",
    "            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "])\n",
    "# 定义训练和验证所用的数据集\n",
    "# API说明：https://paddlex.readthedocs.io/zh_CN/develop/apis/datasets.html#paddlex-datasets-vocdetection\n",
    "# train_dataset = pdx.datasets.VOCDetection(\n",
    "#     data_dir='MyDataset',\n",
    "#     file_list='MyDataset/train_list.txt',\n",
    "#     label_list='MyDataset/labels.txt',\n",
    "#     transforms=train_transforms,\n",
    "#     shuffle=True)\n",
    "# eval_dataset = pdx.datasets.VOCDetection(\n",
    "#     data_dir='MyDataset',\n",
    "#     file_list='MyDataset/val_list.txt',\n",
    "#     label_list='MyDataset/labels.txt',\n",
    "#     transforms=eval_transforms)\n",
    "train_dataset = pdx.datasets.VOCDetection(\n",
    "    data_dir='MyDataset',\n",
    "    file_list='MyDataset/train_list.txt',\n",
    "    label_list='MyDataset/labels.txt',\n",
    "    transforms=train_transforms,\n",
    "    shuffle=True)\n",
    "eval_dataset = pdx.datasets.VOCDetection(\n",
    "    data_dir='MyDataset',\n",
    "    file_list='MyDataset/val_list.txt',\n",
    "    label_list='MyDataset/labels.txt',\n",
    "    transforms=eval_transforms,\n",
    "    shuffle=False)\n",
    "# 初始化模型，并进行训练\n",
    "# 可使用VisualDL查看训练指标，参考https://paddlex.readthedocs.io/zh_CN/develop/train/visualdl.html\n",
    "num_classes = len(train_dataset.labels)\n",
    "\n",
    "# API说明: https://paddlex.readthedocs.io/zh_CN/develop/apis/models/detection.html#paddlex-det-yolov3\n",
    "model = pdx.det.PPYOLO(num_classes=num_classes)\n",
    "\n",
    "# API说明: https://paddlex.readthedocs.io/zh_CN/develop/apis/models/detection.html#train\n",
    "# 各参数介绍与调整说明：https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html\n",
    "# model.train(\n",
    "#     num_epochs=270,\n",
    "#     train_dataset=train_dataset,\n",
    "#     train_batch_size=8,\n",
    "#     eval_dataset=eval_dataset,\n",
    "#     learning_rate=0.000125,\n",
    "#     lr_decay_epochs=[210, 240],\n",
    "#     save_dir='output/ppyolo',\n",
    "#     use_vdl=True)\n",
    "model.train(\n",
    "    num_epochs=2,\n",
    "    train_dataset=train_dataset,\n",
    "    train_batch_size=8,\n",
    "    eval_dataset=eval_dataset,\n",
    "    pretrain_weights='COCO',\n",
    "    learning_rate=0.005 / 12,\n",
    "    warmup_steps=500,\n",
    "    warmup_start_lr=0.0,\n",
    "    save_interval_epochs=5,\n",
    "    lr_decay_epochs=[210, 240],\n",
    "    save_dir='output/ppyolo',\n",
    "    use_vdl=True\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# Python部署\n",
    "PaddleX已经集成了基于Python的高性能预测接口，下面演示单张图片和视频流的预测效果。\n",
    "## 导出预测模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[02-27 14:33:40 MainThread @logger.py:242] Argv: /opt/conda/envs/python35-paddle120-env/bin/paddlex --export_inference --model_dir=./output/ppyolo/best_model --save_dir=./inference_model\n",
      "[02-27 14:33:40 MainThread @utils.py:79] WRN paddlepaddle version: 2.2.2. The dynamic graph version of PARL is under development, not fully tested and supported\n",
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/parl/remote/communication.py:38: DeprecationWarning: 'pyarrow.default_serialization_context' is deprecated as of 2.0.0 and will be removed in a future version. Use pickle or the pyarrow IPC functionality instead.\n",
      "  context = pyarrow.default_serialization_context()\n",
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working\n",
      "  from collections import MutableMapping\n",
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working\n",
      "  from collections import Iterable, Mapping\n",
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working\n",
      "  from collections import Sized\n",
      "W0227 14:33:43.267321   779 device_context.cc:447] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 10.1, Runtime API Version: 10.1\n",
      "W0227 14:33:43.272378   779 device_context.cc:465] device: 0, cuDNN Version: 7.6.\n",
      "2022-02-27 14:33:46 [INFO]\tModel[PPYOLO] loaded.\n",
      "2022-02-27 14:33:46 [WARNING]\t[Important!!!] When exporting inference model for PPYOLO, if fixed_input_shape is not set, it will be forcibly set to [None, 3, 608, 608]. Please ensure image shape after transforms is [3, 608, 608], if not, fixed_input_shape should be specified manually.\n",
      "2022-02-27 14:33:54 [INFO]\tThe model for the inference deployment is saved in ./inference_model/inference_model.\n"
     ]
    }
   ],
   "source": [
    "!paddlex --export_inference --model_dir=./output/ppyolo/best_model --save_dir=./inference_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 单张图片预测\n",
    "选择一张测试集中的图片，查看预测效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-27 14:43:07 [INFO]\tModel[PPYOLO] loaded.\n",
      "------------------ Inference Time Info ----------------------\n",
      "total_time(ms): 514.5999999999999, img_num: 1, batch_size: 1\n",
      "average latency time(ms): 514.60, QPS: 1.943257\n",
      "preprocess_time_per_im(ms): 20.40, inference_time_per_batch(ms): 493.90, postprocess_time_per_im(ms): 0.30\n"
     ]
    }
   ],
   "source": [
    "import paddlex as pdx\n",
    "predictor = pdx.deploy.Predictor('./inference_model/inference_model')\n",
    "result = predictor.predict('./MyDataset/images/hard_hat_workers1457.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt # plt 用于显示图片\n",
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "# 读取原始图片\n",
    "origin_pic = cv2.imread('MyDataset/images/hard_hat_workers1457.png')\n",
    "origin_pic = cv2.cvtColor(origin_pic, cv2.COLOR_BGR2RGB)\n",
    "plt.imshow(origin_pic)\n",
    "plt.axis('off') # 不显示坐标轴\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [140.28770446777344, 0.0, 77.25433349609375, 64.83457946777344],\n",
       "  'score': 0.740882158279419},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [137.58106994628906,\n",
       "   70.61849975585938,\n",
       "   77.08193969726562,\n",
       "   78.67755126953125],\n",
       "  'score': 0.7405170202255249},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [125.77962493896484,\n",
       "   57.83275604248047,\n",
       "   102.42874908447266,\n",
       "   100.99982452392578],\n",
       "  'score': 0.24417127668857574},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [155.45892333984375,\n",
       "   6.716133117675781,\n",
       "   53.15045166015625,\n",
       "   45.98252868652344],\n",
       "  'score': 0.19990143179893494},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [50.015663146972656, 0.0, 46.14247131347656, 29.017532348632812],\n",
       "  'score': 0.14156858623027802},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [162.62725830078125,\n",
       "   12.008363723754883,\n",
       "   39.435028076171875,\n",
       "   45.293500900268555],\n",
       "  'score': 0.12915867567062378},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [158.8102569580078,\n",
       "   4.0262603759765625,\n",
       "   46.311798095703125,\n",
       "   50.94927215576172],\n",
       "  'score': 0.11857397109270096},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [154.3214874267578,\n",
       "   84.2764663696289,\n",
       "   53.26470947265625,\n",
       "   43.73212432861328],\n",
       "  'score': 0.08581605553627014},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [144.2561798095703,\n",
       "   1.0736074447631836,\n",
       "   52.888916015625,\n",
       "   31.40445041656494],\n",
       "  'score': 0.06258692592382431},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [159.97817993164062,\n",
       "   8.540252685546875,\n",
       "   43.9990234375,\n",
       "   52.814788818359375],\n",
       "  'score': 0.06123603135347366},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [141.3635711669922,\n",
       "   69.35801696777344,\n",
       "   65.4342041015625,\n",
       "   82.44548034667969],\n",
       "  'score': 0.0540253110229969},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [134.8566436767578, 0.0, 75.6875, 68.43465423583984],\n",
       "  'score': 0.05007161200046539},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [155.45892333984375,\n",
       "   6.716133117675781,\n",
       "   53.15045166015625,\n",
       "   45.98252868652344],\n",
       "  'score': 0.04724733531475067},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [160.0115509033203,\n",
       "   81.57002258300781,\n",
       "   42.843170166015625,\n",
       "   47.95848083496094],\n",
       "  'score': 0.03671855852007866},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [65.04578399658203, 0.0, 249.5007553100586, 106.35392761230469],\n",
       "  'score': 0.03548653796315193},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [15.65036392211914, 0.0, 123.70586776733398, 36.559776306152344],\n",
       "  'score': 0.0354735292494297},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [65.04578399658203, 0.0, 249.5007553100586, 106.35392761230469],\n",
       "  'score': 0.034009624272584915},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [53.78009033203125, 0.0, 131.44927978515625, 65.889404296875],\n",
       "  'score': 0.031863100826740265},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [155.45892333984375,\n",
       "   6.716133117675781,\n",
       "   53.15045166015625,\n",
       "   45.98252868652344],\n",
       "  'score': 0.03074054792523384},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [122.62628173828125, 0.0, 127.89840698242188, 71.30054473876953],\n",
       "  'score': 0.030525807291269302},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [138.99253845214844,\n",
       "   0.4591331481933594,\n",
       "   42.424407958984375,\n",
       "   31.652565002441406],\n",
       "  'score': 0.030045289546251297},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [123.10062408447266,\n",
       "   86.73519897460938,\n",
       "   124.10359954833984,\n",
       "   67.78561401367188],\n",
       "  'score': 0.029167015105485916},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [65.04578399658203, 0.0, 249.5007553100586, 106.35392761230469],\n",
       "  'score': 0.028663981705904007},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [79.06936645507812, 0.0, 131.43612670898438, 67.42999267578125],\n",
       "  'score': 0.028241347521543503},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [14.544784545898438,\n",
       "   16.51372528076172,\n",
       "   345.4314727783203,\n",
       "   164.29492950439453],\n",
       "  'score': 0.027081161737442017},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [258.5799560546875,\n",
       "   380.3354187011719,\n",
       "   43.6942138671875,\n",
       "   33.664581298828125],\n",
       "  'score': 0.025620749220252037},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [396.7861328125,\n",
       "   194.18099975585938,\n",
       "   18.044921875,\n",
       "   39.304901123046875],\n",
       "  'score': 0.02516269125044346},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [52.958412170410156,\n",
       "   327.53692626953125,\n",
       "   141.37398529052734,\n",
       "   86.46307373046875],\n",
       "  'score': 0.02481335587799549},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [41.63767623901367, 0.0, 109.06311416625977, 60.66790008544922],\n",
       "  'score': 0.0235401950776577},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [145.45950317382812, 0.0, 22.762725830078125, 34.084503173828125],\n",
       "  'score': 0.0221399012953043},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [222.79690551757812,\n",
       "   273.35565185546875,\n",
       "   42.697418212890625,\n",
       "   33.76959228515625],\n",
       "  'score': 0.021690689027309418},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [155.51597595214844,\n",
       "   1.0859622955322266,\n",
       "   50.677032470703125,\n",
       "   34.12466239929199],\n",
       "  'score': 0.02167278341948986},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [166.41744995117188,\n",
       "   5.707624435424805,\n",
       "   24.527862548828125,\n",
       "   45.889841079711914],\n",
       "  'score': 0.02125927433371544},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [137.58106994628906,\n",
       "   70.61849975585938,\n",
       "   77.08193969726562,\n",
       "   78.67755126953125],\n",
       "  'score': 0.02090585045516491},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [61.57426071166992,\n",
       "   115.27313995361328,\n",
       "   118.33565139770508,\n",
       "   184.27510833740234],\n",
       "  'score': 0.02087141014635563},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [14.544784545898438,\n",
       "   16.51372528076172,\n",
       "   345.4314727783203,\n",
       "   164.29492950439453],\n",
       "  'score': 0.019742364063858986},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [144.77291870117188,\n",
       "   60.159149169921875,\n",
       "   76.465576171875,\n",
       "   85.220703125],\n",
       "  'score': 0.01903649792075157},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [122.62628173828125, 0.0, 127.89840698242188, 71.30054473876953],\n",
       "  'score': 0.018894633278250694},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [140.28770446777344, 0.0, 77.25433349609375, 64.83457946777344],\n",
       "  'score': 0.01887269876897335},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [61.57426071166992,\n",
       "   115.27313995361328,\n",
       "   118.33565139770508,\n",
       "   184.27510833740234],\n",
       "  'score': 0.018576571717858315},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [136.04014587402344,\n",
       "   64.68779754638672,\n",
       "   77.26742553710938,\n",
       "   87.75263214111328],\n",
       "  'score': 0.018553519621491432},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [64.08161163330078,\n",
       "   53.52228546142578,\n",
       "   252.75188446044922,\n",
       "   128.71700286865234],\n",
       "  'score': 0.018430430442094803},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [140.28770446777344, 0.0, 77.25433349609375, 64.83457946777344],\n",
       "  'score': 0.01835022121667862},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [154.83447265625,\n",
       "   82.56591033935547,\n",
       "   49.718658447265625,\n",
       "   57.72919464111328],\n",
       "  'score': 0.017337828874588013},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [156.32723999023438,\n",
       "   0.5895442962646484,\n",
       "   28.2913818359375,\n",
       "   33.25969123840332],\n",
       "  'score': 0.016423340886831284},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [125.77962493896484,\n",
       "   57.83275604248047,\n",
       "   102.42874908447266,\n",
       "   100.99982452392578],\n",
       "  'score': 0.016085579991340637},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [95.44447326660156,\n",
       "   0.2930107116699219,\n",
       "   62.53056335449219,\n",
       "   30.879352569580078],\n",
       "  'score': 0.015532967634499073},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [23.032268524169922, 0.0, 102.9518928527832, 60.59950256347656],\n",
       "  'score': 0.015214831568300724},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [18.674888610839844, 0.0, 155.51337432861328, 39.2674446105957],\n",
       "  'score': 0.014487016946077347},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [14.544784545898438,\n",
       "   16.51372528076172,\n",
       "   345.4314727783203,\n",
       "   164.29492950439453],\n",
       "  'score': 0.013817690312862396},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [146.9536590576172,\n",
       "   7.299783706665039,\n",
       "   52.21563720703125,\n",
       "   38.56454277038574],\n",
       "  'score': 0.013690745458006859},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [122.62628173828125, 0.0, 127.89840698242188, 71.30054473876953],\n",
       "  'score': 0.013652936555445194},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [192.283935546875, 0.0, 44.72991943359375, 29.277645111083984],\n",
       "  'score': 0.013574695214629173},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [4.782428741455078, 0.0, 100.84724044799805, 58.91072463989258],\n",
       "  'score': 0.013267547823488712},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [139.587158203125,\n",
       "   1.431025505065918,\n",
       "   23.269775390625,\n",
       "   31.910550117492676],\n",
       "  'score': 0.013189896009862423},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [64.08161163330078,\n",
       "   53.52228546142578,\n",
       "   252.75188446044922,\n",
       "   128.71700286865234],\n",
       "  'score': 0.012975446879863739},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [218.936279296875, 0.0, 109.24114990234375, 81.53606414794922],\n",
       "  'score': 0.012867733836174011},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [154.3214874267578,\n",
       "   84.2764663696289,\n",
       "   53.26470947265625,\n",
       "   43.73212432861328],\n",
       "  'score': 0.012388699688017368},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [146.94247436523438, 0.0, 105.75186157226562, 69.72254943847656],\n",
       "  'score': 0.012259316630661488},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [154.3214874267578,\n",
       "   84.2764663696289,\n",
       "   53.26470947265625,\n",
       "   43.73212432861328],\n",
       "  'score': 0.011961898766458035},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [52.958412170410156,\n",
       "   327.53692626953125,\n",
       "   141.37398529052734,\n",
       "   86.46307373046875],\n",
       "  'score': 0.0117572620511055},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [50.015663146972656, 0.0, 46.14247131347656, 29.017532348632812],\n",
       "  'score': 0.01174673531204462},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [225.86270141601562,\n",
       "   384.7579345703125,\n",
       "   42.32958984375,\n",
       "   29.2420654296875],\n",
       "  'score': 0.011653305031359196},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [168.40603637695312,\n",
       "   18.822650909423828,\n",
       "   24.029541015625,\n",
       "   35.68968200683594],\n",
       "  'score': 0.011473555117845535},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [165.8621063232422,\n",
       "   82.79509735107422,\n",
       "   26.937652587890625,\n",
       "   43.96406555175781],\n",
       "  'score': 0.011289415881037712},\n",
       " {'category_id': 0,\n",
       "  'category': 'head',\n",
       "  'bbox': [64.08161163330078,\n",
       "   53.52228546142578,\n",
       "   252.75188446044922,\n",
       "   128.71700286865234],\n",
       "  'score': 0.010890357196331024},\n",
       " {'category_id': 2,\n",
       "  'category': 'person',\n",
       "  'bbox': [199.87930297851562,\n",
       "   342.22381591796875,\n",
       "   146.9066162109375,\n",
       "   71.77618408203125],\n",
       "  'score': 0.010872340761125088},\n",
       " {'category_id': 1,\n",
       "  'category': 'helmet',\n",
       "  'bbox': [253.64895629882812,\n",
       "   143.78521728515625,\n",
       "   35.37420654296875,\n",
       "   33.625701904296875],\n",
       "  'score': 0.010299021378159523}]"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 视频流预测\n",
    "在AI Studio中不能演示实时效果，因此采用将预测图片保存再合成视频的形式。同样需要重写`paddlex.det.visualize()`方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "??paddlex.det.visualize"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def draw_bbox_mask(image, results, threshold=0.5):\n",
    "    import matplotlib\n",
    "    matplotlib.use('Agg')\n",
    "    import matplotlib as mpl\n",
    "    import matplotlib.figure as mplfigure\n",
    "    import matplotlib.colors as mplc\n",
    "    from matplotlib.backends.backend_agg import FigureCanvasAgg\n",
    "\n",
    "    # refer to  https://github.com/facebookresearch/detectron2/blob/master/detectron2/utils/visualizer.py\n",
    "    def _change_color_brightness(color, brightness_factor):\n",
    "        assert brightness_factor >= -1.0 and brightness_factor <= 1.0\n",
    "        color = mplc.to_rgb(color)\n",
    "        polygon_color = colorsys.rgb_to_hls(*mplc.to_rgb(color))\n",
    "        modified_lightness = polygon_color[1] + (brightness_factor *\n",
    "                                                 polygon_color[1])\n",
    "        modified_lightness = 0.0 if modified_lightness < 0.0 else modified_lightness\n",
    "        modified_lightness = 1.0 if modified_lightness > 1.0 else modified_lightness\n",
    "        modified_color = colorsys.hls_to_rgb(\n",
    "            polygon_color[0], modified_lightness, polygon_color[2])\n",
    "        return modified_color\n",
    "\n",
    "    _SMALL_OBJECT_AREA_THRESH = 1000\n",
    "    # setup figure\n",
    "    width, height = image.shape[1], image.shape[0]\n",
    "    scale = 1\n",
    "    fig = mplfigure.Figure(frameon=False)\n",
    "    dpi = fig.get_dpi()\n",
    "    fig.set_size_inches(\n",
    "        (width * scale + 1e-2) / dpi,\n",
    "        (height * scale + 1e-2) / dpi, )\n",
    "    canvas = FigureCanvasAgg(fig)\n",
    "    ax = fig.add_axes([0.0, 0.0, 1.0, 1.0])\n",
    "    ax.axis(\"off\")\n",
    "    ax.set_xlim(0.0, width)\n",
    "    ax.set_ylim(height)\n",
    "    default_font_size = max(np.sqrt(height * width) // 90, 10 // scale)\n",
    "    linewidth = max(default_font_size / 4, 1)\n",
    "\n",
    "    labels = list()\n",
    "    for dt in np.array(results):\n",
    "        if dt['category'] not in labels:\n",
    "            labels.append(dt['category'])\n",
    "    color_map = get_color_map_list(256)\n",
    "\n",
    "    keep_results = []\n",
    "    areas = []\n",
    "    for dt in np.array(results):\n",
    "        cname, bbox, score = dt['category'], dt['bbox'], dt['score']\n",
    "        if score < threshold:\n",
    "            continue\n",
    "        keep_results.append(dt)\n",
    "        areas.append(bbox[2] * bbox[3])\n",
    "    areas = np.asarray(areas)\n",
    "    sorted_idxs = np.argsort(-areas).tolist()\n",
    "    keep_results = [keep_results[k]\n",
    "                    for k in sorted_idxs] if len(keep_results) > 0 else []\n",
    "\n",
    "    for dt in np.array(keep_results):\n",
    "        cname, bbox, score = dt['category'], dt['bbox'], dt['score']\n",
    "        xmin, ymin, w, h = bbox\n",
    "        xmax = xmin + w\n",
    "        ymax = ymin + h\n",
    "\n",
    "        color = tuple(color_map[labels.index(cname) + 2])\n",
    "        color = [c / 255. for c in color]\n",
    "        # draw bbox\n",
    "        ax.add_patch(\n",
    "            mpl.patches.Rectangle(\n",
    "                (xmin, ymin),\n",
    "                w,\n",
    "                h,\n",
    "                fill=False,\n",
    "                edgecolor=color,\n",
    "                linewidth=linewidth * scale,\n",
    "                alpha=0.8,\n",
    "                linestyle=\"-\", ))\n",
    "\n",
    "        # draw mask\n",
    "        if 'mask' in dt:\n",
    "            mask = dt['mask']\n",
    "            mask = np.ascontiguousarray(mask)\n",
    "            res = cv2.findContours(\n",
    "                mask.astype(\"uint8\"), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)\n",
    "            hierarchy = res[-1]\n",
    "            alpha = 0.5\n",
    "            if hierarchy is not None:\n",
    "                has_holes = (hierarchy.reshape(-1, 4)[:, 3] >= 0).sum() > 0\n",
    "                res = res[-2]\n",
    "                res = [x.flatten() for x in res]\n",
    "                res = [x for x in res if len(x) >= 6]\n",
    "                for segment in res:\n",
    "                    segment = segment.reshape(-1, 2)\n",
    "                    edge_color = mplc.to_rgb(color) + (1, )\n",
    "                    polygon = mpl.patches.Polygon(\n",
    "                        segment,\n",
    "                        fill=True,\n",
    "                        facecolor=mplc.to_rgb(color) + (alpha, ),\n",
    "                        edgecolor=edge_color,\n",
    "                        linewidth=max(default_font_size // 15 * scale, 1), )\n",
    "                    ax.add_patch(polygon)\n",
    "\n",
    "        # draw label\n",
    "        text_pos = (xmin, ymin)\n",
    "        horiz_align = \"left\"\n",
    "        instance_area = w * h\n",
    "        if (instance_area < _SMALL_OBJECT_AREA_THRESH * scale or\n",
    "                h < 40 * scale):\n",
    "            if ymin >= height - 5:\n",
    "                text_pos = (xmin, ymin)\n",
    "            else:\n",
    "                text_pos = (xmin, ymax)\n",
    "        height_ratio = h / np.sqrt(height * width)\n",
    "        font_size = (np.clip((height_ratio - 0.02) / 0.08 + 1, 1.2,\n",
    "                             2) * 0.5 * default_font_size)\n",
    "        text = \"{} {:.2f}\".format(cname, score)\n",
    "        color = np.maximum(list(mplc.to_rgb(color)), 0.2)\n",
    "        color[np.argmax(color)] = max(0.8, np.max(color))\n",
    "        color = _change_color_brightness(color, brightness_factor=0.7)\n",
    "        ax.text(\n",
    "            text_pos[0],\n",
    "            text_pos[1],\n",
    "            text,\n",
    "            size=font_size * scale,\n",
    "            family=\"sans-serif\",\n",
    "            bbox={\n",
    "                \"facecolor\": \"black\",\n",
    "                \"alpha\": 0.8,\n",
    "                \"pad\": 0.7,\n",
    "                \"edgecolor\": \"none\"\n",
    "            },\n",
    "            verticalalignment=\"top\",\n",
    "            horizontalalignment=horiz_align,\n",
    "            color=color,\n",
    "            zorder=10,\n",
    "            rotation=0, )\n",
    "\n",
    "    s, (width, height) = canvas.print_to_buffer()\n",
    "    buffer = np.frombuffer(s, dtype=\"uint8\")\n",
    "\n",
    "    img_rgba = buffer.reshape(height, width, 4)\n",
    "    rgb, alpha = np.split(img_rgba, [3], axis=2)\n",
    "\n",
    "    try:\n",
    "        import numexpr as ne\n",
    "        visualized_image = ne.evaluate(\n",
    "            \"image * (1 - alpha / 255.0) + rgb * (alpha / 255.0)\")\n",
    "    except ImportError:\n",
    "        alpha = alpha.astype(\"float32\") / 255.0\n",
    "        visualized_image = image * (1 - alpha) + rgb * alpha\n",
    "\n",
    "    visualized_image = visualized_image.astype(\"uint8\")\n",
    "\n",
    "    return visualized_image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def get_color_map_list(num_classes):\n",
    "    \"\"\" Returns the color map for visualizing the segmentation mask,\n",
    "        which can support arbitrary number of classes.\n",
    "    Args:\n",
    "        num_classes: Number of classes\n",
    "    Returns:\n",
    "        The color map\n",
    "    \"\"\"\n",
    "    color_map = num_classes * [0, 0, 0]\n",
    "    for i in range(0, num_classes):\n",
    "        j = 0\n",
    "        lab = i\n",
    "        while lab:\n",
    "            color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j))\n",
    "            color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j))\n",
    "            color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j))\n",
    "            j += 1\n",
    "            lab >>= 3\n",
    "    color_map = [color_map[i:i + 3] for i in range(0, len(color_map), 3)]\n",
    "    return color_map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def visualize(image, result, threshold=0.5, img_num=0,save_dir='./'):\n",
    "    \"\"\"\n",
    "        Visualize bbox and mask results\n",
    "    \"\"\"\n",
    "\n",
    "    if isinstance(image, np.ndarray):\n",
    "        image_name = str(img_num) + '.jpg'\n",
    "    else:\n",
    "        image_name = os.path.split(image)[-1]\n",
    "        image = cv2.imread(image)\n",
    "\n",
    "    image = draw_bbox_mask(image, result, threshold=threshold)\n",
    "    if save_dir is not None:\n",
    "        if not os.path.exists(save_dir):\n",
    "            os.makedirs(save_dir)\n",
    "        out_path = os.path.join(save_dir, '{}'.format(image_name))\n",
    "        cv2.imwrite(out_path, image)\n",
    "        print('The visualized result is saved as {}'.format(out_path))\n",
    "    else:\n",
    "        return image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import time\n",
    "import os\n",
    "import sys\n",
    "import colorama\n",
    "from colorama import init\n",
    "import paddlex\n",
    "\n",
    "init(autoreset=True)\n",
    "levels = {0: 'ERROR', 1: 'WARNING', 2: 'INFO', 3: 'DEBUG'}\n",
    "\n",
    "\n",
    "def log(level=2, message=\"\", use_color=False):\n",
    "    current_time = time.time()\n",
    "    time_array = time.localtime(current_time)\n",
    "    current_time = time.strftime(\"%Y-%m-%d %H:%M:%S\", time_array)\n",
    "    if paddlex.log_level >= level:\n",
    "        if use_color:\n",
    "            print(\"\\033[1;31;40m{} [{}]\\t{}\\033[0m\".format(\n",
    "                current_time, levels[level], message).encode(\"utf-8\").decode(\n",
    "                    \"latin1\"))\n",
    "        else:\n",
    "            print(\"{} [{}]\\t{}\".format(current_time, levels[level], message)\n",
    "                  .encode(\"utf-8\").decode(\"latin1\"))\n",
    "        sys.stdout.flush()\n",
    "\n",
    "\n",
    "def debug(message=\"\", use_color=False):\n",
    "    log(level=3, message=message, use_color=use_color)\n",
    "\n",
    "\n",
    "def info(message=\"\", use_color=False):\n",
    "    log(level=2, message=message, use_color=use_color)\n",
    "\n",
    "\n",
    "def warning(message=\"\", use_color=True):\n",
    "    log(level=1, message=message, use_color=use_color)\n",
    "\n",
    "\n",
    "def error(message=\"\", use_color=True, exit=True):\n",
    "    log(level=0, message=message, use_color=use_color)\n",
    "    if exit:\n",
    "        sys.exit(-1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The visualized result is saved as hatdet/374.jpg\r"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "import paddlex as pdx\n",
    "import numpy as np\n",
    "import colorsys\n",
    "import os\n",
    "\n",
    "predictor = pdx.deploy.Predictor('./inference_model/inference_model')\n",
    "cap = cv2.VideoCapture('./hatdet.mp4')\n",
    "i = 1\n",
    "while cap.isOpened():\n",
    "    ret, frame = cap.read()\n",
    "    if ret:\n",
    "        result = predictor.predict(frame)\n",
    "        print(i)\n",
    "        vis_img = visualize(frame, result, threshold=0.4, img_num=i, save_dir='hatdet')\n",
    "        i += 1\n",
    "        # 本地环境可以实时查看安全帽检测效果\n",
    "        # cv2.imshow('hatdet', vis_img)\n",
    "        if cv2.waitKey(1) & 0xFF == ord('q'):\n",
    "            break\n",
    "    else:\n",
    "        break\n",
    "cap.release()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 将图片合成视频\n",
    "!ffmpeg -f image2 -i ./hatdet/%d.jpg -vcodec libx264 -r 30 ppyolo.mp4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "我们可以发现，pp-yolo其实把普通的帽子也识别成安全帽了，相比yolov3_darknet53，各有改进空间。\n",
    "<center class = \"half\">\n",
    "<img src=https://ai-studio-static-online.cdn.bcebos.com/8e7e1caf21bc45b5b33f83b7a59f7ed042717bb7c6f64fef80722fd48785077f width=50% align=left><img src=https://ai-studio-static-online.cdn.bcebos.com/a417f8c783f243348f0107e416f4e2873403c90305a64a139fb4fa673382b9a2 width=50% align=right>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<video src=\"ppyolo.mp4\" controls  >\n",
       "      Your browser does not support the <code>video</code> element.\n",
       "    </video>"
      ],
      "text/plain": [
       "<IPython.core.display.Video object>"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import IPython\r\n",
    "IPython.display.Video('ppyolo.mp4')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "#  模型加密部署（Linux平台）\n",
    "## 简介\n",
    "来源：[PaddleX官方文档](https://github.com/paddlepaddle/PaddleX/blob/develop/docs/deploy/server/encryption.md)\n",
    "\n",
    "（1）加密算法的选择和支持的库\n",
    "\n",
    "一般使用OpenSSL库来支持数据的加解密，OpenSSL提供了大量的加解密算法，包括对称加密算法（AES等）和非对称加密算法（RSA等）。\n",
    "\n",
    "两种算法使用的场景不同，**非对称加密算法一般应用于数字签名和密钥协商的场景下**，而**对称加密算法一般应用于纯数据加密场景**，性能更优。在对模型的加密过程中使用对称加密算法。\n",
    "\n",
    "> 笔者注：简单来说，非对称加密算法保密性更好，暴力破解难度远大于对称加密算法，但这是以时间为代价换来的，非对称加密算法不适合大规模数据的加密。\n",
    "\n",
    "以下对模型加密场景实现的说明中以开发一个C/C++库为基础，采用AES对称加密算法，为了加解密前后能够快速判断解密是否成功，使用AES-GCM加解密模式，在密钥的安全性上使用长度为256位的密钥数据。\n",
    "\n",
    "（2）实现模型保护的一般步骤：\n",
    "\n",
    "![file](https://gitee.com/paddlepaddle/PaddleX/raw/develop/docs/deploy/images/encryption_process.png)\n",
    "\n",
    "下面是对提供的C/C++加解密库内部实现的中文描述，参考以下步骤可以实现一套加解密库来适应自己的场景并通过内存数据加载到Paddle Inference预测库中\n",
    "\n",
    "> 1）考虑到加密的模型文件解密后需要从内存加载数据，使用conbine的模式生成模型文件和参数文件。\n",
    ">\n",
    "> 2）项目集成OpenSSL，使用静态库的形式。\n",
    ">\n",
    "> 3）实现AES算法接口，借助OpenSSL提供的EVP接口，在EVP接口中指定算法类型，算法使用对称加解密算法中的AES，加解密模式使用AES-GCM， 密钥长度为256位，AES-GCM的实现可以参考官方提供的例子自己进行封装接口：[AES-GCM实现](https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption)。\n",
    ">\n",
    "> 4）利用OpenSSL库实现SHA256摘要算法，这部分下面有用（可选）。关于SHA256的hash计算可以参考OpenSSL提供的example：[OpenSSL 信息摘要例子](https://wiki.openssl.org/index.php/EVP_Message_Digests)。\n",
    ">\n",
    "> 5）在模型加密环节直接对model文件和params文件的数据内容进行加密后保存到新的文件，为了新的文件能够被区分和可迭代，除了加密后的数据外还添加了头部信息，比如为了判断该文件类型使用固定的魔数作为文件的开头；为了便于后面需求迭代写入版本号以示区别；为了能够在解密时判断是否采用了相同的密钥将加密时的密钥进行SHA256计算后存储；这三部分构成了目前加密后文件的头部信息。加密后的文件包含头部信息 + 密文信息。\n",
    ">\n",
    "> 6）在模型解密环节根据加密后的文件读取相关的加密数据到内存中，对内存数据使用AES算法进行解密，注意解密时需要采用与加密时一致的加密算法和加密的模式，以及密钥的数据和长度，否则会导致解密后数据错误。\n",
    ">\n",
    "> 7）集成模型预测的C/C++库，在具体使用预测时一般涉及paddle::AnalysisConfig和paddle::Predictor，为了能够从内存数据中直接load解密后的模型明文数据（避免模型解密后创建临时文件），这里需要将AnalysisConfig的模型加载函数从SetModel替换为SetModelBuffer来实现从内存中加载模型数据。\n",
    "\n",
    "需要注意的是，在本方案中，密钥集成在上层预测服务的代码中。故模型的安全强度等同于代码抵御逆向调试的强度。为了保护密钥和模型的安全，开发者还需对自己的应用进行加固保护。常见的应用加固手段有：代码混淆，二进制文件加壳 等等，亦或将加密机制更改为AES白盒加密技术来保护密钥。这类技术领域内有大量商业和开源产品可供选择，此处不一一赘述。\n",
    "## 下载并解压加密工具\n",
    "[Linux版本 PaddleX模型加密工具](https://bj.bcebos.com/paddlex/tools/1.2.0/paddlex-encryption.zip)，在本地编译脚本时会自动下载该版本加密工具，但是在AI Studio编译失败，因此这里选择手动下载。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--2022-02-27 14:51:39--  https://bj.bcebos.com/paddlex/tools/1.2.0/paddlex-encryption.zip\n",
      "Resolving bj.bcebos.com (bj.bcebos.com)... 182.61.200.195, 182.61.200.229, 2409:8c04:1001:1002:0:ff:b001:368a\n",
      "Connecting to bj.bcebos.com (bj.bcebos.com)|182.61.200.195|:443... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 3972031 (3.8M) [application/octet-stream]\n",
      "Saving to: ‘paddlex-encryption.zip’\n",
      "\n",
      "paddlex-encryption. 100%[===================>]   3.79M  17.1MB/s    in 0.2s    \n",
      "\n",
      "2022-02-27 14:51:39 (17.1 MB/s) - ‘paddlex-encryption.zip’ saved [3972031/3972031]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!wget https://bj.bcebos.com/paddlex/tools/1.2.0/paddlex-encryption.zip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Archive:  paddlex-encryption.zip\n",
      "   creating: paddlex-encryption/\n",
      "   creating: paddlex-encryption/include/\n",
      "  inflating: paddlex-encryption/include/paddle_model_encrypt.h  \n",
      "  inflating: paddlex-encryption/include/paddle_model_decrypt.h  \n",
      "  inflating: paddlex-encryption/include/model_code.h  \n",
      "  inflating: paddlex-encryption/.DS_Store  \n",
      "  inflating: paddlex-encryption/README  \n",
      "   creating: paddlex-encryption/tool/\n",
      "  inflating: paddlex-encryption/tool/paddle_encrypt_tool  \n",
      "   creating: paddlex-encryption/lib/\n",
      "  inflating: paddlex-encryption/lib/libpmodel-encrypt.so  \n",
      "  inflating: paddlex-encryption/lib/libpmodel-decrypt.so  \n"
     ]
    }
   ],
   "source": [
    "!unzip paddlex-encryption.zip"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 导出PaddleX加密模型\n",
    "加密完成后，加密过的模型会保存至指定的`-save_dir`下，包含`__model__.encrypted`、`__params__.encrypted`和`model.yml`三个文件，同时生成密钥信息，命令输出如下，密钥为`onHEuCBj4kYLuRmDdPtbODXpvdYaVRLRB/eWhkopx8U=`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Output: Encryption key: \n",
      "\tO2G0zMSicFycd3LjZ9/2lsjZhlEZQ28w5sayToQp4FI=\n",
      "Success, Encrypt __model__, __params__ to /home/aistudio/paddlex_encrypted_model(dir) success!\n"
     ]
    }
   ],
   "source": [
    "! ./paddlex-encryption/tool/paddle_encrypt_tool -model_dir /home/aistudio/inference_model -save_dir /home/aistudio/paddlex_encrypted_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 本地Linux平台部署加密模型\n",
    "> 注：以下内容需要在本地环境完成，其中一个关键原因是AI Studio上的gcc版本过高，同时没有`sudo`权限降低gcc版本。\n",
    "### PaddleX文档要求的前置条件\n",
    "- G++ 4.8.2 ~ 4.9.4\n",
    "- CUDA 9.0 / CUDA 10.0, CUDNN 7+ （仅在使用GPU版本的预测库时需要）\n",
    "- CMake 3.0+\n",
    "\n",
    "实际完成本地测试的部署环境\n",
    "- G++ 4.8.5\n",
    "- CUDA 10.2，CUDNN 7.6.5\n",
    "- CMake 3.10.2\n",
    "- 有网络环境\n",
    "\n",
    "### 安装GCC\n",
    "参考：[GCC降级和升级](https://blog.csdn.net/qianqiying/article/details/81585136)\n",
    "\n",
    "本地环境GCC版本过高，编译时出现了报错，因此对GCC进行降级\n",
    "1. 安装gcc4.8\n",
    "```bash\n",
    "sudo apt-get install -y gcc-4.8\n",
    "sudo apt-get install -y g++-4.8\n",
    "```\n",
    "2. 重新建立软连接\n",
    "```bash\n",
    "cd /usr/bin    #进入/usr/bin文件夹下\n",
    "sudo rm -r gcc  #移除之前的软连接\n",
    "sudo ln -sf gcc-4.8 gcc #建立gcc4.7的软连接\n",
    "sudo rm -r g++  #同gcc\n",
    "sudo ln -sf g++-4.8 g++\n",
    "```\n",
    "3. 确认gcc版本\n",
    "```bash\n",
    "gcc --version #查看gcc版本\n",
    "```\n",
    "```bash\n",
    "gcc (Ubuntu 4.8.5-4ubuntu8) 4.8.5\n",
    "Copyright (C) 2015 Free Software Foundation, Inc.\n",
    "This is free software; see the source for copying conditions.  There is NO\n",
    "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
    "```\n",
    "### 安装Linux预测库\n",
    "\n",
    "#### 1. 拉取PaddleX模型库\n",
    "\n",
    "```bash\n",
    "git clone https://gitee.com/paddlepaddle/PaddleX.git\n",
    "```\n",
    "**说明**：其中`C++`预测代码在`/root/projects/PaddleX/deploy/cpp` 目录，该目录不依赖任何`PaddleX`下其他目录。\n",
    "\n",
    "#### 2. 下载PaddlePaddle C++ 预测库 paddle_inference\n",
    "\n",
    "根据[PaddleX文档介绍](https://gitee.com/paddlepaddle/PaddleX/blob/develop/docs/deploy/server/cpp/linux.md)与[飞桨官网的说明](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/build_and_install_lib_cn.html#id12)直接选择合适的版本进行安装\n",
    "> **注意:** 预编译版本除`nv-jetson-cuda10-cudnn7.5-trt5` 以外其它包都是基于`GCC 4.8.5`编译，使用高版本`GCC`可能存在 `ABI`兼容性问题，建议降级或[自行编译预测库](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_guide/inference_deployment/inference/build_and_install_lib_cn.html#id12)。\n",
    "\n",
    "在本文中，使用的是`ubuntu14.04_cuda10.0_cudnn7_avx_mkl`预测库(1.8.4版本)\n",
    "\n",
    "下载安装包后，解压到`PaddleX/deploy/cpp`目录下，下载并解压后`PaddleX/deploy/cpp/fluid_inference`目录包含内容为：\n",
    "```\n",
    "fluid_inference\n",
    "├── paddle # paddle核心库和头文件\n",
    "|\n",
    "├── third_party # 第三方依赖库和头文件\n",
    "|\n",
    "└── version.txt # 版本和编译信息\n",
    "```\n",
    "\n",
    "#### 3. 编译\n",
    "将工作目录切换到`PaddleX/deploy/cpp`目录下之后，只需执行build脚本即可开始编译。\n",
    "```bash\n",
    "sh scripts/build.sh\n",
    "```\n",
    "$\\color{#FF3030}{但是！需要先配置好`scripts/build.sh`，否则在预测时会出现报错！}$\n",
    "脚本注释比较清晰：\n",
    "```bash\n",
    "# 是否使用GPU(即是否使用 CUDA)\n",
    "WITH_GPU=OFF\n",
    "# 使用MKL or openblas\n",
    "WITH_MKL=ON\n",
    "# 是否集成 TensorRT(仅WITH_GPU=ON 有效)\n",
    "WITH_TENSORRT=OFF\n",
    "# TensorRT 的路径，如果需要集成TensorRT，需修改为您实际安装的TensorRT路径\n",
    "TENSORRT_DIR=/root/projects/TensorRT/\n",
    "# Paddle 预测库路径, 请修改为您实际安装的预测库路径\n",
    "PADDLE_DIR=/root/projects/fluid_inference\n",
    "# Paddle 的预测库是否使用静态库来编译\n",
    "# 使用TensorRT时，Paddle的预测库通常为动态库\n",
    "WITH_STATIC_LIB=OFF\n",
    "# CUDA 的 lib 路径\n",
    "CUDA_LIB=/usr/local/cuda/lib64\n",
    "# CUDNN 的 lib 路径\n",
    "CUDNN_LIB=/usr/local/cuda/lib64\n",
    "\n",
    "# 是否加载加密后的模型\n",
    "WITH_ENCRYPTION=ON\n",
    "# 加密工具的路径, 如果使用自带预编译版本可不修改\n",
    "sh $(pwd)/scripts/bootstrap.sh # 下载预编译版本的加密工具\n",
    "ENCRYPTION_DIR=$(pwd)/paddlex-encryption\n",
    "\n",
    "# OPENCV 路径, 如果使用自带预编译版本可不修改\n",
    "sh $(pwd)/scripts/bootstrap.sh  # 下载预编译版本的opencv\n",
    "OPENCV_DIR=$(pwd)/deps/opencv3gcc4.8/\n",
    "\n",
    "# 以下无需改动\n",
    "rm -rf build\n",
    "mkdir -p build\n",
    "cd build\n",
    "cmake .. \\\n",
    "    -DWITH_GPU=${WITH_GPU} \\\n",
    "    -DWITH_MKL=${WITH_MKL} \\\n",
    "    -DWITH_TENSORRT=${WITH_TENSORRT} \\\n",
    "    -DWITH_ENCRYPTION=${WITH_ENCRYPTION} \\\n",
    "    -DTENSORRT_DIR=${TENSORRT_DIR} \\\n",
    "    -DPADDLE_DIR=${PADDLE_DIR} \\\n",
    "    -DWITH_STATIC_LIB=${WITH_STATIC_LIB} \\\n",
    "    -DCUDA_LIB=${CUDA_LIB} \\\n",
    "    -DCUDNN_LIB=${CUDNN_LIB} \\\n",
    "    -DENCRYPTION_DIR=${ENCRYPTION_DIR} \\\n",
    "    -DOPENCV_DIR=${OPENCV_DIR}\n",
    "make\n",
    "```\n",
    "\n",
    "PP-YOLO模型使用MKLDNN预测库在CPU上预测会报错，由于本地是conda环境，因此手动配置了CUDA和CUDNN路径，并选择使用静态库预测，主要修改内容如下：\n",
    "\n",
    "```bash\n",
    "# 使用GPU(即是否使用 CUDA)\n",
    "WITH_GPU=ON\n",
    "# 使用MKL or openblas\n",
    "WITH_MKL=ON\n",
    "# 不集成 TensorRT(仅WITH_GPU=ON 有效)\n",
    "WITH_TENSORRT=OFF\n",
    "# TensorRT 的路径，如果需要集成TensorRT，需修改为您实际安装的TensorRT路径\n",
    "TENSORRT_DIR=$(pwd)/TensorRT/\n",
    "# Paddle 预测库路径, 请修改为您实际安装的预测库路径\n",
    "PADDLE_DIR=$(pwd)/fluid_inference\n",
    "# Paddle 的预测库使用静态库编译\n",
    "WITH_STATIC_LIB=ON\n",
    "# CUDA 的 lib 路径，使用miniconda环境的绝对路径，供参考\n",
    "CUDA_LIB=/home/aistudio/miniconda3/envs/paddle/lib\n",
    "# CUDNN 的 lib 路径，使用miniconda环境的绝对路径，供参考\n",
    "CUDNN_LIB=/home/aistudio/miniconda3/envs/paddle/lib\n",
    "```\n",
    "\n",
    "这时候，就可以执行脚本，等待编译完成了：\n",
    "\n",
    "![file](https://ai-studio-static-online.cdn.bcebos.com/19740dbc134d488597cc8009554f7a19d51822e373dd40f390af3830c91962b6)\n",
    "\n",
    "**注意：** linux环境下编译会自动下载OPENCV, PaddleX-Encryption和YAML，如果编译环境无法访问外网，可手动下载：\n",
    "\n",
    "- [opencv3.4.6gcc4.8ffmpeg.tar.gz2](https://bj.bcebos.com/paddleseg/deploy/opencv3.4.6gcc4.8ffmpeg.tar.gz2)\n",
    "- [paddlex-encryption.zip](https://bj.bcebos.com/paddlex/tools/1.2.0/paddlex-encryption.zip)\n",
    "- [yaml-cpp.zip](https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip)\n",
    "\n",
    "opencv3gcc4.8.tar.bz2文件下载后解压，然后在script/build.sh中指定`OPENCE_DIR`为解压后的路径。\n",
    "\n",
    "paddlex-encryption.zip文件下载后解压，然后在script/build.sh中指定`ENCRYPTION_DIR`为解压后的路径。\n",
    "\n",
    "yaml-cpp.zip文件下载后无需解压，在cmake/yaml.cmake中将`URL https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip` 中的网址，改为下载文件的路径。\n",
    "\n",
    "修改脚本设置好主要参数后，执行`build`脚本：\n",
    " ```shell\n",
    " sh ./scripts/build.sh\n",
    " ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 预测与可视化\n",
    "\n",
    "#### 1. 下载加密模型目录paddlex_encrypted_model到本地并解压缩\n",
    "\n",
    "* 编译成功后，目标检测图片预测demo的可执行程序为`build/demo/detector`，其主要命令参数说明如下：\n",
    "\n",
    "|  参数   | 说明  |\n",
    "|  ----  | ----  |\n",
    "| model_dir  | 导出的预测模型所在路径 |\n",
    "| image  | 要预测的图片文件路径 |\n",
    "| image_list  | 按行存储图片路径的.txt文件 |\n",
    "| use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |\n",
    "| use_trt  | 是否使用 TensorRT 预测, 支持值为0或1(默认值为0) |\n",
    "| use_mkl  | 是否使用 MKL加速CPU预测, 支持值为0或1(默认值为1) |\n",
    "| mkl_thread_num | MKL推理的线程数，默认为cpu处理器个数 |\n",
    "| gpu_id  | GPU 设备ID, 默认值为0 |\n",
    "| save_dir | 保存可视化结果的路径, 默认值为\"output\"，**classfier无该参数** |\n",
    "| key | 加密过程中产生的密钥信息，默认值为\"\"表示加载的是未加密的模型 |\n",
    "| batch_size | 预测的批量大小，默认为1 |\n",
    "| thread_num | 预测的线程数，默认为cpu处理器个数 |\n",
    "\n",
    "* 编译成功后，目标检测视频预测demo的可执行程序为`build/demo/video_detector`，其主要命令参数说明如下：\n",
    "\n",
    "|  参数   | 说明  |\n",
    "|  ----  | ----  |\n",
    "| model_dir  | 导出的预测模型所在路径 |\n",
    "| use_camera | 是否使用摄像头预测，支持值为0或1(默认值为0) |\n",
    "| camera_id | 摄像头设备ID，默认值为0 |\n",
    "| video_path | 视频文件的路径 |\n",
    "| use_gpu  | 是否使用 GPU 预测, 支持值为0或1(默认值为0) |\n",
    "| use_trt  | 是否使用 TensorRT 预测, 支持值为0或1(默认值为0) |\n",
    "| use_mkl  | 是否使用 MKL加速CPU预测, 支持值为0或1(默认值为1) |\n",
    "| mkl_thread_num | MKL推理的线程数，默认为cpu处理器个数 |\n",
    "| gpu_id  | GPU 设备ID, 默认值为0 |\n",
    "| show_result | 对视频文件做预测时，是否在屏幕上实时显示预测可视化结果(因加入了延迟处理，故显示结果不能反映真实的帧率)，支持值为0或1(默认值为0) |\n",
    "| save_result | 是否将每帧的预测可视结果保存为视频文件，支持值为0或1(默认值为1) |\n",
    "| save_dir | 保存可视化结果的路径, 默认值为\"output\"|\n",
    "| key | 加密过程中产生的密钥信息，默认值为\"\"表示加载的是未加密的模型 |\n",
    "\n",
    "**注意：若系统无GUI，则不要将show_result设置为1。当使用摄像头预测时，按`ESC`键可关闭摄像头并推出预测程序。**\n",
    "\n",
    "#### 2. 测试加密效果\n",
    "\n",
    "- 不提供Key报错信息\n",
    "\n",
    "![file](https://ai-studio-static-online.cdn.bcebos.com/ae57e058f922482cb1bd7d4290975912ad600960fbe4409cbb945c74da942950)\n",
    "\n",
    "- 提供错误的Key报错信息\n",
    "\n",
    "![file](https://ai-studio-static-online.cdn.bcebos.com/874016b28d5c49fd8276d6fae6b0e0c86c69bd15c51f44cbb2082c12886893e0)\n",
    "\n",
    "#### 3. 图片预测效果\n",
    "\n",
    "预测时指定加密模型导出时的正确密钥`onHEuCBj4kYLuRmDdPtbODXpvdYaVRLRB/eWhkopx8U=`后，单张图片的预测命令及效果如下（注意，当前目录已切换到`PaddleX/deploy/cpp`）：\n",
    "\n",
    "```bash\n",
    "./build/demo/detector --model_dir=./paddlex_encrypted_model --image=./hard_hat_workers1025.png --key=onHEuCBj4kYLuRmDdPtbODXpvdYaVRLRB/eWhkopx8U= --use_gpu=1 --save_dir=output --batch_size=2 --thread_num=2\n",
    "```\n",
    "\n",
    "![file](https://ai-studio-static-online.cdn.bcebos.com/3815e7dcd7ce498096ebb7f319c0c139896c9661e4d24d7c8a6ac24e67dfc026)\n",
    "\n",
    "![file](https://ai-studio-static-online.cdn.bcebos.com/e3369fce0eaa4aa3b41d3473f13f6d0221dd701dcc7b4fe9a0bb3887147291e0)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# HubServing轻量级服务化部署\n",
    "**注意：使用此方式部署，需确保自己Python环境中PaddleHub的版本高于1.8.0,因此需要将AI Studio中的Paddlehub升级。**\n",
    "- [轻量级服务化部署](https://github.com/paddlepaddle/PaddleX/blob/develop/docs/deploy/hub_serving.md)\n",
    "- [PaddleHub-Serving](https://github.com/PaddlePaddle/PaddleHub/blob/develop/docs/tutorial/serving.md)\n",
    "## 部署模型准备\n",
    "\n",
    "部署模型的格式均为目录下包含`__model__`，`__params__`和`model.yml`三个文件，也就是`inference_model`目录下的文件格式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Requirement already satisfied: paddlehub in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (2.0.4)\n",
      "Collecting paddlehub\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/1e/86/7184a1c76a3ca9bb47cbf838df6479164c21849da751452b5d11eea4140d/paddlehub-2.2.0-py3-none-any.whl (212 kB)\n",
      "     |████████████████████████████████| 212 kB 5.5 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: rarfile in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (3.1)\n",
      "Requirement already satisfied: opencv-python in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (4.1.1.26)\n",
      "Requirement already satisfied: paddlenlp>=2.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (2.1.1)\n",
      "Requirement already satisfied: colorama in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (0.4.4)\n",
      "Requirement already satisfied: numpy in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (1.19.5)\n",
      "Requirement already satisfied: pyzmq in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (22.3.0)\n",
      "Requirement already satisfied: gunicorn>=19.10.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (20.0.4)\n",
      "Requirement already satisfied: colorlog in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (4.1.0)\n",
      "Requirement already satisfied: matplotlib in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (2.2.3)\n",
      "Requirement already satisfied: pyyaml in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (5.1.2)\n",
      "Requirement already satisfied: easydict in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (1.9)\n",
      "Requirement already satisfied: Pillow in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (8.2.0)\n",
      "Collecting paddle2onnx>=0.5.1\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/91/b9/c87d6c35f946a965b72ebccb078ec9e43016749f4aa3cd5eb9297c8199b3/paddle2onnx-0.9.1-py3-none-any.whl (95 kB)\n",
      "     |████████████████████████████████| 95 kB 5.3 MB/s             \n",
      "\u001b[?25hRequirement already satisfied: tqdm in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (4.27.0)\n",
      "Requirement already satisfied: flask>=1.1.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (1.1.1)\n",
      "Requirement already satisfied: packaging in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (21.3)\n",
      "Requirement already satisfied: filelock in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (3.0.12)\n",
      "Requirement already satisfied: visualdl>=2.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlehub) (2.2.3)\n",
      "Requirement already satisfied: click>=5.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.0->paddlehub) (7.0)\n",
      "Requirement already satisfied: Werkzeug>=0.15 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.0->paddlehub) (0.16.0)\n",
      "Requirement already satisfied: Jinja2>=2.10.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.0->paddlehub) (2.11.0)\n",
      "Requirement already satisfied: itsdangerous>=0.24 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flask>=1.1.0->paddlehub) (1.1.0)\n",
      "Requirement already satisfied: setuptools>=3.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from gunicorn>=19.10.0->paddlehub) (56.2.0)\n",
      "Requirement already satisfied: six in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddle2onnx>=0.5.1->paddlehub) (1.16.0)\n",
      "Collecting onnx<=1.9.0\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/3f/9b/54c950d3256e27f970a83cd0504efb183a24312702deed0179453316dbd0/onnx-1.9.0-cp37-cp37m-manylinux2010_x86_64.whl (12.2 MB)\n",
      "     |████████████████████████████████| 12.2 MB 5.4 MB/s            \n",
      "\u001b[?25hRequirement already satisfied: protobuf in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddle2onnx>=0.5.1->paddlehub) (3.14.0)\n",
      "Requirement already satisfied: jieba in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlenlp>=2.0.0->paddlehub) (0.42.1)\n",
      "Requirement already satisfied: multiprocess in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlenlp>=2.0.0->paddlehub) (0.70.11.1)\n",
      "Requirement already satisfied: seqeval in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlenlp>=2.0.0->paddlehub) (1.2.2)\n",
      "Requirement already satisfied: h5py in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlenlp>=2.0.0->paddlehub) (2.9.0)\n",
      "Requirement already satisfied: paddlefsl==1.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlenlp>=2.0.0->paddlehub) (1.0.0)\n",
      "Requirement already satisfied: requests~=2.24.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from paddlefsl==1.0.0->paddlenlp>=2.0.0->paddlehub) (2.24.0)\n",
      "Requirement already satisfied: Flask-Babel>=1.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.0.0->paddlehub) (1.0.0)\n",
      "Requirement already satisfied: shellcheck-py in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.0.0->paddlehub) (0.7.1.1)\n",
      "Requirement already satisfied: pre-commit in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.0.0->paddlehub) (1.21.0)\n",
      "Requirement already satisfied: flake8>=3.7.9 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.0.0->paddlehub) (4.0.1)\n",
      "Requirement already satisfied: pandas in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.0.0->paddlehub) (1.1.5)\n",
      "Requirement already satisfied: bce-python-sdk in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from visualdl>=2.0.0->paddlehub) (0.8.53)\n",
      "Requirement already satisfied: pytz in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddlehub) (2019.3)\n",
      "Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddlehub) (2.8.2)\n",
      "Requirement already satisfied: cycler>=0.10 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddlehub) (0.10.0)\n",
      "Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddlehub) (1.1.0)\n",
      "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from matplotlib->paddlehub) (3.0.7)\n",
      "Requirement already satisfied: importlib-metadata<4.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.0.0->paddlehub) (4.2.0)\n",
      "Requirement already satisfied: pycodestyle<2.9.0,>=2.8.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.0.0->paddlehub) (2.8.0)\n",
      "Requirement already satisfied: mccabe<0.7.0,>=0.6.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.0.0->paddlehub) (0.6.1)\n",
      "Requirement already satisfied: pyflakes<2.5.0,>=2.4.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from flake8>=3.7.9->visualdl>=2.0.0->paddlehub) (2.4.0)\n",
      "Requirement already satisfied: Babel>=2.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from Flask-Babel>=1.0.0->visualdl>=2.0.0->paddlehub) (2.8.0)\n",
      "Requirement already satisfied: MarkupSafe>=0.23 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from Jinja2>=2.10.1->flask>=1.1.0->paddlehub) (2.0.1)\n",
      "Requirement already satisfied: typing-extensions>=3.6.2.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from onnx<=1.9.0->paddle2onnx>=0.5.1->paddlehub) (4.0.1)\n",
      "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from requests~=2.24.0->paddlefsl==1.0.0->paddlenlp>=2.0.0->paddlehub) (1.25.6)\n",
      "Requirement already satisfied: idna<3,>=2.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from requests~=2.24.0->paddlefsl==1.0.0->paddlenlp>=2.0.0->paddlehub) (2.8)\n",
      "Requirement already satisfied: chardet<4,>=3.0.2 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from requests~=2.24.0->paddlefsl==1.0.0->paddlenlp>=2.0.0->paddlehub) (3.0.4)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from requests~=2.24.0->paddlefsl==1.0.0->paddlenlp>=2.0.0->paddlehub) (2019.9.11)\n",
      "Requirement already satisfied: future>=0.6.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from bce-python-sdk->visualdl>=2.0.0->paddlehub) (0.18.0)\n",
      "Requirement already satisfied: pycryptodome>=3.8.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from bce-python-sdk->visualdl>=2.0.0->paddlehub) (3.9.9)\n",
      "Requirement already satisfied: dill>=0.3.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from multiprocess->paddlenlp>=2.0.0->paddlehub) (0.3.3)\n",
      "Requirement already satisfied: cfgv>=2.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.0.0->paddlehub) (2.0.1)\n",
      "Requirement already satisfied: toml in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.0.0->paddlehub) (0.10.0)\n",
      "Requirement already satisfied: nodeenv>=0.11.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.0.0->paddlehub) (1.3.4)\n",
      "Requirement already satisfied: aspy.yaml in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.0.0->paddlehub) (1.3.0)\n",
      "Requirement already satisfied: identify>=1.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.0.0->paddlehub) (1.4.10)\n",
      "Requirement already satisfied: virtualenv>=15.2 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from pre-commit->visualdl>=2.0.0->paddlehub) (16.7.9)\n",
      "Requirement already satisfied: scikit-learn>=0.21.3 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from seqeval->paddlenlp>=2.0.0->paddlehub) (0.23.2)\n",
      "Requirement already satisfied: zipp>=0.5 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from importlib-metadata<4.3->flake8>=3.7.9->visualdl>=2.0.0->paddlehub) (3.7.0)\n",
      "Requirement already satisfied: joblib>=0.11 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from scikit-learn>=0.21.3->seqeval->paddlenlp>=2.0.0->paddlehub) (0.14.1)\n",
      "Requirement already satisfied: scipy>=0.19.1 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from scikit-learn>=0.21.3->seqeval->paddlenlp>=2.0.0->paddlehub) (1.6.3)\n",
      "Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from scikit-learn>=0.21.3->seqeval->paddlenlp>=2.0.0->paddlehub) (2.1.0)\n",
      "Installing collected packages: onnx, paddle2onnx, paddlehub\n",
      "  Attempting uninstall: paddlehub\n",
      "    Found existing installation: paddlehub 2.0.4\n",
      "    Uninstalling paddlehub-2.0.4:\n",
      "      Successfully uninstalled paddlehub-2.0.4\n",
      "Successfully installed onnx-1.9.0 paddle2onnx-0.9.1 paddlehub-2.2.0\n",
      "WARNING: You are using pip version 21.3.1; however, version 22.0.3 is available.\n",
      "You should consider upgrading via the '/opt/conda/envs/python35-paddle120-env/bin/python -m pip install --upgrade pip' command.\n"
     ]
    }
   ],
   "source": [
    "!pip install paddlehub -U"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型转换\n",
    "\n",
    "将`PaddleX`的`Inference Model`转换成`PaddleHub`的预训练模型，使用命令`hub convert`即可一键转换，对此命令的说明如下：\n",
    "|参数\t|用途|\n",
    "| -------- | -------- |\n",
    "|--model_dir/-m |`PaddleX Inference Model`所在的目录|\n",
    "|--module_name/-n |生成预训练模型的名称|\n",
    "|--module_version/-v |生成预训练模型的版本，默认为`1.0.0`|\n",
    "|--output_dir/-o |生成预训练模型的存放位置，默认为`{module_name}_{timestamp}`|"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The converted module is stored in `hatdet_1645945597.0586548`.\r\n"
     ]
    }
   ],
   "source": [
    "!hub convert --model_dir inference_model \\\n",
    "              --module_name hatdet \\\n",
    "              --module_version 1.0 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型安装\n",
    "\n",
    "将模型转换得到的`.tar.gz`格式的预训练模型压缩包，在进行部署之前需要先安装到本机，使用命令`hub install`一键安装"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Traceback (most recent call last):\r\n",
      "  File \"/opt/conda/envs/python35-paddle120-env/bin/hub\", line 8, in <module>\r\n",
      "    sys.exit(execute())\r\n",
      "  File \"/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlehub/commands/utils.py\", line 78, in execute\r\n",
      "    status = 0 if com['_entry']().execute(sys.argv[idx:]) else 1\r\n",
      "  File \"/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlehub/commands/install.py\", line 55, in execute\r\n",
      "    manager.install(name=name, version=version, ignore_env_mismatch=args.ignore_env_mismatch)\r\n",
      "  File \"/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlehub/module/manager.py\", line 177, in install\r\n",
      "    with lock:\r\n",
      "  File \"/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/filelock.py\", line 323, in __enter__\r\n",
      "    self.acquire()\r\n",
      "  File \"/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/filelock.py\", line 271, in acquire\r\n",
      "    self._acquire()\r\n",
      "  File \"/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/filelock.py\", line 384, in _acquire\r\n",
      "    fd = os.open(self._lock_file, open_mode)\r\n",
      "FileNotFoundError: [Errno 2] No such file or directory: '/home/aistudio/.paddlehub/tmp/hatdet_1600013222.3970025/hatdet.tar.gz'\r\n"
     ]
    }
   ],
   "source": [
    "!hub install hatdet_1600013222.3970025/hatdet.tar.gz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型部署\n",
    "\n",
    "打开终端1，输入`hub serving start -m hatdet`完成安全帽检测模型的一键部署"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 预测结果与后处理效果展示\n",
    "可以看出，对比[yolov3_darknet53](https://aistudio.baidu.com/aistudio/projectdetail/742090)的预测效果，pp-yolo在nms上可能用了大量的tricks，导致输出results特别多。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "ConnectionError",
     "evalue": "HTTPConnectionPool(host='127.0.0.1', port=8866): Max retries exceeded with url: /predict/hatdet (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f7b8e7ca290>: Failed to establish a new connection: [Errno 111] Connection refused'))",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mConnectionRefusedError\u001b[0m                    Traceback (most recent call last)",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/connection.py\u001b[0m in \u001b[0;36m_new_conn\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    156\u001b[0m             conn = connection.create_connection(\n\u001b[0;32m--> 157\u001b[0;31m                 \u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_dns_host\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mextra_kw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    158\u001b[0m             )\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/util/connection.py\u001b[0m in \u001b[0;36mcreate_connection\u001b[0;34m(address, timeout, source_address, socket_options)\u001b[0m\n\u001b[1;32m     83\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0merr\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 84\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/util/connection.py\u001b[0m in \u001b[0;36mcreate_connection\u001b[0;34m(address, timeout, source_address, socket_options)\u001b[0m\n\u001b[1;32m     73\u001b[0m                 \u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msource_address\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m             \u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msa\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     75\u001b[0m             \u001b[0;32mreturn\u001b[0m \u001b[0msock\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mConnectionRefusedError\u001b[0m: [Errno 111] Connection refused",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31mNewConnectionError\u001b[0m                        Traceback (most recent call last)",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/connectionpool.py\u001b[0m in \u001b[0;36murlopen\u001b[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)\u001b[0m\n\u001b[1;32m    671\u001b[0m                 \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 672\u001b[0;31m                 \u001b[0mchunked\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mchunked\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    673\u001b[0m             )\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/connectionpool.py\u001b[0m in \u001b[0;36m_make_request\u001b[0;34m(self, conn, method, url, timeout, chunked, **httplib_request_kw)\u001b[0m\n\u001b[1;32m    386\u001b[0m         \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 387\u001b[0;31m             \u001b[0mconn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mhttplib_request_kw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    388\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/http/client.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, body, headers, encode_chunked)\u001b[0m\n\u001b[1;32m   1243\u001b[0m         \u001b[0;34m\"\"\"Send a complete request to the server.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1244\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1245\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/http/client.py\u001b[0m in \u001b[0;36m_send_request\u001b[0;34m(self, method, url, body, headers, encode_chunked)\u001b[0m\n\u001b[1;32m   1289\u001b[0m             \u001b[0mbody\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_encode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'body'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1290\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mendheaders\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1291\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/http/client.py\u001b[0m in \u001b[0;36mendheaders\u001b[0;34m(self, message_body, encode_chunked)\u001b[0m\n\u001b[1;32m   1238\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0mCannotSendHeader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1239\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send_output\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmessage_body\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1240\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/http/client.py\u001b[0m in \u001b[0;36m_send_output\u001b[0;34m(self, message_body, encode_chunked)\u001b[0m\n\u001b[1;32m   1025\u001b[0m         \u001b[0;32mdel\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1026\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1027\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/http/client.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m    965\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_open\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 966\u001b[0;31m                 \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    967\u001b[0m             \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/connection.py\u001b[0m in \u001b[0;36mconnect\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    183\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 184\u001b[0;31m         \u001b[0mconn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_new_conn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    185\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_prepare_conn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/connection.py\u001b[0m in \u001b[0;36m_new_conn\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    168\u001b[0m             raise NewConnectionError(\n\u001b[0;32m--> 169\u001b[0;31m                 \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"Failed to establish a new connection: %s\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    170\u001b[0m             )\n",
      "\u001b[0;31mNewConnectionError\u001b[0m: <urllib3.connection.HTTPConnection object at 0x7f7b8e7ca290>: Failed to establish a new connection: [Errno 111] Connection refused",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31mMaxRetryError\u001b[0m                             Traceback (most recent call last)",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/requests/adapters.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001b[0m\n\u001b[1;32m    448\u001b[0m                     \u001b[0mretries\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmax_retries\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 449\u001b[0;31m                     \u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    450\u001b[0m                 )\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/connectionpool.py\u001b[0m in \u001b[0;36murlopen\u001b[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)\u001b[0m\n\u001b[1;32m    719\u001b[0m             retries = retries.increment(\n\u001b[0;32m--> 720\u001b[0;31m                 \u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merror\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_pool\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_stacktrace\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexc_info\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    721\u001b[0m             )\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/urllib3/util/retry.py\u001b[0m in \u001b[0;36mincrement\u001b[0;34m(self, method, url, response, error, _pool, _stacktrace)\u001b[0m\n\u001b[1;32m    435\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mnew_retry\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_exhausted\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 436\u001b[0;31m             \u001b[0;32mraise\u001b[0m \u001b[0mMaxRetryError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_pool\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merror\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mResponseError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcause\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    437\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mMaxRetryError\u001b[0m: HTTPConnectionPool(host='127.0.0.1', port=8866): Max retries exceeded with url: /predict/hatdet (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f7b8e7ca290>: Failed to establish a new connection: [Errno 111] Connection refused'))",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31mConnectionError\u001b[0m                           Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_101/1041083149.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     26\u001b[0m     \u001b[0;31m# 发送HTTP请求\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     27\u001b[0m     \u001b[0murl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"http://127.0.0.1:8866/predict/hatdet\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 28\u001b[0;31m     \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrequests\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpost\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     29\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     30\u001b[0m     \u001b[0;31m# 打印预测结果，注意，r.json()[\"results\"]本身就是一个数组，要取到对应图片的预测结果，需指定元素位置，如r.json()[\"results\"][0]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/requests/api.py\u001b[0m in \u001b[0;36mpost\u001b[0;34m(url, data, json, **kwargs)\u001b[0m\n\u001b[1;32m    117\u001b[0m     \"\"\"\n\u001b[1;32m    118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 119\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'post'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mjson\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    120\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    121\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/requests/api.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(method, url, **kwargs)\u001b[0m\n\u001b[1;32m     59\u001b[0m     \u001b[0;31m# cases, and look like a memory leak in others.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     60\u001b[0m     \u001b[0;32mwith\u001b[0m \u001b[0msessions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSession\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0msession\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 61\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     62\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     63\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/requests/sessions.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001b[0m\n\u001b[1;32m    528\u001b[0m         }\n\u001b[1;32m    529\u001b[0m         \u001b[0msend_kwargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msettings\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 530\u001b[0;31m         \u001b[0mresp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprep\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0msend_kwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    531\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    532\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mresp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/requests/sessions.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, request, **kwargs)\u001b[0m\n\u001b[1;32m    641\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    642\u001b[0m         \u001b[0;31m# Send the request\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 643\u001b[0;31m         \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0madapter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    644\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    645\u001b[0m         \u001b[0;31m# Total elapsed time of the request (approximately)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/requests/adapters.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001b[0m\n\u001b[1;32m    514\u001b[0m                 \u001b[0;32mraise\u001b[0m \u001b[0mSSLError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrequest\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    515\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 516\u001b[0;31m             \u001b[0;32mraise\u001b[0m \u001b[0mConnectionError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrequest\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    517\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    518\u001b[0m         \u001b[0;32mexcept\u001b[0m \u001b[0mClosedPoolError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mConnectionError\u001b[0m: HTTPConnectionPool(host='127.0.0.1', port=8866): Max retries exceeded with url: /predict/hatdet (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f7b8e7ca290>: Failed to establish a new connection: [Errno 111] Connection refused'))"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 864x576 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# coding: utf8\n",
    "%matplotlib inline\n",
    "import requests\n",
    "import json\n",
    "import cv2\n",
    "import base64\n",
    "import numpy as np\n",
    "import colorsys\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "plt.figure(figsize=(12,8))\n",
    "\n",
    "def cv2_to_base64(image):\n",
    "    data = cv2.imencode('.png', image)[1]\n",
    "    return base64.b64encode(data.tostring()).decode('utf8')\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    # 获取图片的base64编码格式\n",
    "    img1 = cv2_to_base64(cv2.imread(\"MyDataset/images/hard_hat_workers1957.png\"))\n",
    "    img2 = cv2_to_base64(cv2.imread(\"MyDataset/images/hard_hat_workers1457.png\"))\n",
    "    data = {'images': [img1, img2]}\n",
    "    # data = {'images': [img1]}\n",
    "    # 指定content-type\n",
    "    headers = {\"Content-type\": \"application/json\"}\n",
    "    # 发送HTTP请求\n",
    "    url = \"http://127.0.0.1:8866/predict/hatdet\"\n",
    "    r = requests.post(url=url, headers=headers, data=json.dumps(data))\n",
    "\n",
    "    # 打印预测结果，注意，r.json()[\"results\"]本身就是一个数组，要取到对应图片的预测结果，需指定元素位置，如r.json()[\"results\"][0]\n",
    "    print(r.json()[\"results\"])\n",
    "    # 使用重写的visualize()方法完成预测结果后处理\n",
    "    # 显示第一张图片的预测效果\n",
    "    image = visualize(cv2.imread('MyDataset/images/hard_hat_workers1957.png'),r.json()[\"results\"][0], save_dir=None)\n",
    "    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
    "    plt.imshow(image)\n",
    "    plt.axis('off') # 不显示坐标轴\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 下一步计划\n",
    "- 安全帽佩戴检测模型在树莓派上的部署实现"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
